diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e257b466..27074c84 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,38 +1,23 @@ -If you wanna contribute please submit a PR to one of the [iris-contrib organisation's](https://github.com/iris-contrib) projects. +## Code Of Conduct -##### Note that I do not accept pull requests here and that I use the issue tracker for bug reports and proposals only. Please ask questions on the [https://kataras.rocket.chat/channel/iris][Chat] or [http://stackoverflow.com/](http://stackoverflow.com). +The community should respect and follow our new [Code of Conduct](https://github.com/kataras/iris/blob/master/CODE-OF-CONDUCT.md). ## Before Submitting an Issue -First, please do a search in [open issues](http://support.iris-go.com) to see if the issue or feature request has already been filed. If there is an issue add your comments to this issue. +Navigate through [issues](https://github.com/kataras/issues) and check if it has been already filled by other person. -The Iris project is distributed across multiple repositories, try to file the issue against the correct repository, -- [Community iris-specific middleware](https://github.com/iris-contrib/middleware/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [App reloader and command line tool](https://github.com/kataras/rizla/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [View Engine](https://github.com/kataras/go-template/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [Sessions](https://github.com/kataras/go-sessions/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [About docs.iris-go.com](https://github.com/iris-contrib/gitbook/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [About examples](https://github.com/iris-contrib/examples/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [Iris main(core)](http://support.iris-go.com) +Before post a new issue, please do an upgrade: -Before post a new issue do an iris upgrade: - -- Delete `$GOPATH/src/gopkg.in/kataras` -- Open shell and execute the command: `go get -u gopkg.in/kataras/iris.v6/iris` +- Delete `$GOPATH/src/github.com/kataras` +- Open shell and execute the command: `go get -u github.com/kataras/iris` - Try to re-produce the issue -- If the issue still exists, then post the issue with the necessary information. +To be aware of the framework's changes and updates please **[star](https://github.com/kataras/iris/stargazers)** and **[watch](https://github.com/kataras/iris/watchers)** the repository. -If the issue is after an upgrade, please read the [HISTORY.md](https://github.com/kataras/iris/blob/v6/HISTORY.md) for any breaking-changes and fixes. - -The author answers the same day, perhaps the same hour you post the issue. - -It is impossible to notify each user on every change, so to be aware of the framework's changes and be notify about updates -please **star** or **watch** the repository. - -If your issue is a closed-personal question then please ask that question on [community chat][Chat]. +Do not discuss things that they're not relative to the framework, keep Github issues useful for newcomers. A Github issue should exists to solve or report a problem. +> If you want to talk about something else that can't be inside Github issues please [chat](https://gitter.im/iris-go/Lobby) with us. ## Writing Good Bug Reports and Feature Requests @@ -47,5 +32,3 @@ The more information you can provide, the more likely someone will be successful * Version of Iris * Errors in the Terminal/Console * When you have glide/godep installed, can you reproduce the issue when starting Iris' station without these? - -[Chat]: https://kataras.rocket.chat/channel/iris diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 3a7b0f2c..421e4d59 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,6 @@ Please answer these questions before submitting your issue. Thanks! -- [x] I have read the [GopherBOOk](http://gopherbook.iris-go.com), [Examples](https://github.com/iris-contrib/examples), [Contributing File](https://github.com/kataras/iris/blob/v6/.github/CONTRIBUTING.md) and **I'm sure that this issue is not [posted](http://support.iris-go.com)** before. +- [x] I have read the [_examples](https://github.com/kataras/iris/tree/master/_examples), [Contributing File](https://github.com/kataras/iris/blob/master/.github/CONTRIBUTING.md). ### What version of Go are you using, minimum 1.8 (`go version`)? diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1a799390..8e9730b3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,20 +1,6 @@ -This repository follows the official [golang](https://github.com/golang/go#contributing) guidelines for the contributions: +I'd love to see more contributions! -##### Note that I do not accept pull requests here and that I use the issue tracker proposals only. Please ask questions on the http://support.iris-go.com or [https://kataras.rocket.chat/channel/iris][chat]. - -Iris' features are not directly adopted to Iris. A component's feature/change should be tested for some time before being part of the Iris which users install. - -Do PRs at the iris' components instead of the core. -You can find many repositories to help Iris with your contribution. The [iris-contrib](https://github.com/iris-contrib) is open for any -kind of PR, community is 100% responsible for the whole organisation. - -- [Community iris-specific middleware](https://github.com/iris-contrib/middleware/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [App reloader and command line tool](https://github.com/kataras/rizla/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [View Engine](https://github.com/kataras/go-template/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [Sessions](https://github.com/kataras/go-sessions/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [For docs.iris-go.com](https://github.com/iris-contrib/gitbook/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) -- [For examples](https://github.com/iris-contrib/examples/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) +If you are interested in contributing to the Iris project, please read and understand the [Code of Conduct](https://github.com/kataras/iris/blob/master/CODE-OF-CONDUCT.md) before submitting your [PR](https://github.com/kataras/iris/pulls). - **Only one term to future authors**: A contributor should be responsible and answer to the future users' issues that are relative to her/his code. PR's authors must provide adequate support. -**Users is the most important part, we, as software authors, have to respect them. I don't accept any form of contempt to them(users) by anyone.** +Don't forget to, first, open an [issue](https://github.com/kataras/iris) to discuss what changes you're willing to push. \ No newline at end of file diff --git a/.gitignore b/.gitignore index e2d21c23..28021e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -.settings -.project +# my own configuration for vs code, my lovely editor, if someone of you want this, contact with me .vscode -specs +# turn off these for now .github +specs \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9015ecd4..84d1e298 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ language: go - +os: + - linux + - osx go: - - go1.8 - -go_import_path: gopkg.in/kataras/iris.v6 + - go1.8 + - tip +go_import_path: github.com/kataras/iris +before_install: + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libgtk-3-dev libappindicator3-dev ; fi \ No newline at end of file diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md new file mode 100644 index 00000000..829bdf6a --- /dev/null +++ b/CODE-OF-CONDUCT.md @@ -0,0 +1,41 @@ +# Iris Open Source Code of Conduct + +This code of conduct outlines expectations for participation in kataras-managed open source communities, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all. People violating this code of conduct may be banned from the community. + +Our open source communities strive to: + +* **Be friendly and patient**: Remember you might not be communicating in someone else's primary spoken or programming language, and others may not have your level of understanding. +* **Be welcoming**: Our communities welcome and support people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. +* **Be respectful**: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to: + * Violent threats or language. + * Discriminatory or derogatory jokes and language. + * Posting sexually explicit or violent material. + * Posting, or threatening to post, people's personally identifying information ("doxing"). + * Insults, especially those using discriminatory terms or slurs. + * Behavior that could be perceived as sexual attention. + * Advocating for or encouraging any of the above behaviors. +* **Understand disagreements**: Disagreements, both social and technical, are useful learning opportunities. Seek to understand the other viewpoints and resolve differences constructively. +* This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter. + +## Scope + +This code of conduct applies to all repos and communities for kataras-managed open source projects regardless of whether or not the repo explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing a project or its community. Examples include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + + +## Reporting Code of Conduct Issues + +We encourage all communities to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by contacting kataras@tutanota.com. + +In your report please include: + +* Your contact information. +* Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. +* Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment. +* Any additional information that may be helpful. + + +All reports will be reviewed by [kataras](https://github.com/kataras) and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, I may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team. + +Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in **unacceptable behavior**, the review team may take any action they deem appropriate, **including a permanent ban from the community**. + +This code of conduct is based on the [template](http://todogroup.org/opencodeofconduct) established by the [TODO Group](http://todogroup.org/) and used by numerous other large communities (e.g., [Facebook](https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://engineering.twitter.com/opensource/code-of-conduct), [GitHub](http://todogroup.org/opencodeofconduct/#opensource@github.com)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/). \ No newline at end of file diff --git a/DONATIONS.md b/DONATIONS.md deleted file mode 100644 index 4a037e73..00000000 --- a/DONATIONS.md +++ /dev/null @@ -1,66 +0,0 @@ -The main purpose of donations to open source is to say "thank you" to the developer rather than actually advancing the project. - - -Open source projects don’t need the money like those charities do—and so it’s usually phrased like: *Buy the developer a cup of coffee*. - - - - -# Buy me a cup of coffee? - -Iris is free and open source but developing it has taken thousands of hours of my time and a large part of my sanity. If you feel this web framework useful to you, it would go a great way to ensuring that I can afford to take the time to continue to develop it. - - -I spend all my time in the construction of Iris, therefore I have no income value. - -Feel free to send **any** amount through paypal - -[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) - - -Some of the benefits are listed here: - -- Your github username, after your approval, is visible here as a "premium" community member. -- Access to the 'donors' [private chat room](https://kataras.rocket.chat/group/donors) gives you real-time assistance by Iris' Author. - -## More about your donations - -[Juan Sebastián Suárez Valencia](https://github.com/Juanses) donated 20 EUR at September 11 - -[Bob Lee](https://github.com/li3p) donated 20 EUR at September 16 - -[Celso Luiz](https://github.com/celsosz) donated 50 EUR at September 29 - -[Ankur Srivastava](https://github.com/ansrivas) donated 20 EUR at October 2 - -[Damon Zhao](https://github.com/se77en) donated 20 EUR at October 21 - -[exponity - consulting & digital transformation](https://github.com/exponity) donated 30 EUR at November 4 - -[Thomas Fritz](https://github.com/thomasfr) donated 25 EUR at Jenuary 8 of 2017 - -[Thanos V.](http://mykonosbiennale.com/) donated 20 EUR at Jenuary 16 of 2017 - -[George Opritescu](https://github.com/International) donated 20 EUR at February 7 of 2017 - -[Lex Tang](https://github.com/lexrus) donated 20 EUR at February 22 of 2017 - -[Conrad Steenberg](https://github.com/hengestone) donated 25 EUR at March 23 of 2017 - -*ANONYMOUS* donated 356 EUR, last anonymous donation at March 22 of 2017 - -> The names, shown at the [legends](https://github.com/kataras/iris#legends-) list, are sorted by **date** and **NOT by the amount** of the donation. - - -> *ANONYMOUS*: People who donate but don't want to be shown here. *ANONYMOUS* are listed as one group instead of an individual entry, in order to protect their exact date of their donation. - -# Thanks for your gratitude and finance help ♡ \ No newline at end of file diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 00000000..b8bf13d5 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,309 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/BurntSushi/toml" + packages = ["."] + revision = "b26d9c308763d68093482582cea63d69be07a0f0" + +[[projects]] + branch = "master" + name = "github.com/Joker/jade" + packages = ["."] + revision = "35b3f5bdbcc920cd31f4870536dbc63be8530541" + +[[projects]] + branch = "master" + name = "github.com/Unknwon/i18n" + packages = ["."] + revision = "8372b908b5876d26cfa46a85fc4851b981dad102" + +[[projects]] + branch = "master" + name = "github.com/ajg/form" + packages = ["."] + revision = "523a5da1a92f01b01f840b61689c0340a0243532" + +[[projects]] + branch = "master" + name = "github.com/aymerick/raymond" + packages = [".","ast","lexer","parser"] + revision = "72acac2207479d21dd45898c2a4264246c818148" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/eknkc/amber" + packages = [".","parser"] + revision = "f0d8fdb67f9f4a2c0d02fb6ce4830b8b6754de10" + +[[projects]] + branch = "master" + name = "github.com/esemplastic/unis" + packages = [".","logger"] + revision = "6e30ed034e8cb31227b24057d030fdd0261bcfc6" + +[[projects]] + branch = "master" + name = "github.com/fatih/structs" + packages = ["."] + revision = "74a29b9fac7397d933f47e33eba94d2d83a464a2" + +[[projects]] + branch = "master" + name = "github.com/flosch/pongo2" + packages = ["."] + revision = "1d0f0d3af150c4a65dfd424d742f7374819e7d29" + +[[projects]] + name = "github.com/garyburd/redigo" + packages = ["internal","redis"] + revision = "433969511232c397de61b1442f9fd49ec06ae9ba" + version = "v1.1.0" + +[[projects]] + branch = "master" + name = "github.com/gavv/monotime" + packages = ["."] + revision = "47d58efa69556a936a3c15eb2ed42706d968ab01" + +[[projects]] + branch = "master" + name = "github.com/getlantern/context" + packages = ["."] + revision = "624d99b1798d7c5375ea1d3ca4c5b04d58f7c775" + +[[projects]] + branch = "master" + name = "github.com/getlantern/errors" + packages = ["."] + revision = "99fa440517e8f3d1e4cd8d6dbed6b41f4c1ed3d6" + +[[projects]] + branch = "master" + name = "github.com/getlantern/filepersist" + packages = ["."] + revision = "c5f0cd24e7991579ba6f5f1bd20a1ad2c9f06cd4" + +[[projects]] + branch = "master" + name = "github.com/getlantern/golog" + packages = ["."] + revision = "cca714f7feb5df8e455f409b549d384441ac4578" + +[[projects]] + branch = "master" + name = "github.com/getlantern/hex" + packages = ["."] + revision = "083fba3033ad473db3dd31c9bb368473d37581a7" + +[[projects]] + branch = "master" + name = "github.com/getlantern/hidden" + packages = ["."] + revision = "d52a649ab33af200943bb599898dbdcfdbc94cb7" + +[[projects]] + branch = "master" + name = "github.com/getlantern/ops" + packages = ["."] + revision = "b70875f5d689a9438bca72aefd7142a2af889b18" + +[[projects]] + branch = "master" + name = "github.com/getlantern/stack" + packages = ["."] + revision = "02f928aad224fbccd50d66edd776fc9d1e9f2f2b" + +[[projects]] + branch = "master" + name = "github.com/getlantern/systray" + packages = ["."] + revision = "0068f6ae40ea39bfd683043e8452024097fff0e4" + +[[projects]] + branch = "master" + name = "github.com/google/go-querystring" + packages = ["query"] + revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" + +[[projects]] + name = "github.com/gorilla/websocket" + packages = ["."] + revision = "3ab3a8b8831546bd18fd182c20687ca853b2bb13" + version = "v1.1.0" + +[[projects]] + name = "github.com/imdario/mergo" + packages = ["."] + revision = "3e95a51e0639b4cf372f2ccf74c86749d747fbdc" + version = "0.2.2" + +[[projects]] + branch = "master" + name = "github.com/imkira/go-interpol" + packages = ["."] + revision = "5accad8134979a6ac504d456a6c7f1c53da237ca" + +[[projects]] + branch = "master" + name = "github.com/iris-contrib/httpexpect" + packages = ["."] + revision = "ed44d3c477393b96a598a38730f128dffee33068" + +[[projects]] + branch = "master" + name = "github.com/juju/errors" + packages = ["."] + revision = "8234c829496aaeae67c5c71235a2cbcc289e960a" + +[[projects]] + branch = "master" + name = "github.com/klauspost/compress" + packages = ["flate","gzip"] + revision = "f3dce52e0576655d55fd69e74b63da96ad1108f3" + +[[projects]] + branch = "master" + name = "github.com/klauspost/cpuid" + packages = ["."] + revision = "09cded8978dc9e80714c4d85b0322337b0a1e5e0" + +[[projects]] + branch = "master" + name = "github.com/microcosm-cc/bluemonday" + packages = ["."] + revision = "e79763773ab6222ca1d5a7cbd9d62d83c1f77081" + +[[projects]] + branch = "master" + name = "github.com/monoculum/formam" + packages = ["."] + revision = "334e05a3c7f95d500715d7c0405937ac933b28cb" + +[[projects]] + branch = "master" + name = "github.com/moul/http2curl" + packages = ["."] + revision = "4e24498b31dba4683efb9d35c1c8a91e2eda28c8" + +[[projects]] + branch = "master" + name = "github.com/oxtoacart/bpool" + packages = ["."] + revision = "4e1c5567d7c2dd59fa4c7c83d34c2f3528b025d6" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + branch = "master" + name = "github.com/russross/blackfriday" + packages = ["."] + revision = "0ba0f2b6ed7c475a92e4df8641825cb7a11d1fa3" + +[[projects]] + branch = "master" + name = "github.com/satori/go.uuid" + packages = ["."] + revision = "5bf94b69c6b68ee1b541973bb8e1144db23a194b" + +[[projects]] + branch = "master" + name = "github.com/sergi/go-diff" + packages = ["diffmatchpatch"] + revision = "feef008d51ad2b3778f85d387ccf91735543008d" + +[[projects]] + branch = "master" + name = "github.com/stretchr/testify" + packages = ["assert","require"] + revision = "f6abca593680b2315d2075e0f5e2a9751e3f431a" + +[[projects]] + branch = "master" + name = "github.com/valyala/bytebufferpool" + packages = ["."] + revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7" + +[[projects]] + branch = "master" + name = "github.com/xeipuuv/gojsonpointer" + packages = ["."] + revision = "6fe8760cad3569743d51ddbb243b26f8456742dc" + +[[projects]] + branch = "master" + name = "github.com/xeipuuv/gojsonreference" + packages = ["."] + revision = "e02fc20de94c78484cd5ffb007f8af96be030a45" + +[[projects]] + branch = "master" + name = "github.com/xeipuuv/gojsonschema" + packages = ["."] + revision = "0c8571ac0ce161a5feb57375a9cdf148c98c0f70" + +[[projects]] + branch = "master" + name = "github.com/yalp/jsonpath" + packages = ["."] + revision = "31a79c7593bb93eb10b163650d4a3e6ca190e4dc" + +[[projects]] + name = "github.com/yudai/gojsondiff" + packages = [".","formatter"] + revision = "7b1b7adf999dab73a6eb02669c3d82dbb27a3dd6" + version = "1.0.0" + +[[projects]] + branch = "master" + name = "github.com/yudai/golcs" + packages = ["."] + revision = "ecda9a501e8220fae3b4b600c3db4b0ba22cfc68" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["acme","acme/autocert"] + revision = "e1a4589e7d3ea14a3352255d04b6f1a418845e5e" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["html","html/atom","idna","publicsuffix"] + revision = "e4fa1c5465ad6111f206fc92186b8c83d64adbe1" + +[[projects]] + branch = "master" + name = "golang.org/x/text" + packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] + revision = "ccbd3f7822129ff389f8ca4858a9b9d4d910531c" + +[[projects]] + name = "gopkg.in/ini.v1" + packages = ["."] + revision = "d3de07a94d22b4a0972deb4b96d790c2c0ce8333" + version = "v1.28.0" + +[[projects]] + branch = "v2" + name = "gopkg.in/yaml.v2" + packages = ["."] + revision = "cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "415eba3367a3b497ce9c74414ba802d5902d5b7fefc7dfc99cff6af102ec40fe" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 00000000..81361cc8 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,179 @@ + +## Gopkg.toml example (these lines may be deleted) + +## "metadata" defines metadata about the project that could be used by other independent +## systems. The metadata defined here will be ignored by dep. +# [metadata] +# key1 = "value that convey data to other systems" +# system1-data = "value that is used by a system" +# system2-data = "value that is used by another system" + +## "required" lists a set of packages (not projects) that must be included in +## Gopkg.lock. This list is merged with the set of packages imported by the current +## project. Use it when your project needs a package it doesn't explicitly import - +## including "main" packages. +# required = ["github.com/user/thing/cmd/thing"] + +## "ignored" lists a set of packages (not projects) that are ignored when +## dep statically analyzes source code. Ignored packages can be in this project, +## or in a dependency. +# ignored = ["github.com/user/project/badpkg"] + +## Constraints are rules for how directly imported projects +## may be incorporated into the depgraph. They are respected by +## dep whether coming from the Gopkg.toml of the current project or a dependency. +# [[constraint]] +## Required: the root import path of the project being constrained. +# name = "github.com/user/project" +# +## Recommended: the version constraint to enforce for the project. +## Only one of "branch", "version" or "revision" can be specified. +# version = "1.0.0" +# branch = "master" +# revision = "abc123" +# +## Optional: an alternate location (URL or import path) for the project's source. +# source = "https://github.com/myfork/package.git" +# +## "metadata" defines metadata about the dependency or override that could be used +## by other independent systems. The metadata defined here will be ignored by dep. +# [metadata] +# key1 = "value that convey data to other systems" +# system1-data = "value that is used by a system" +# system2-data = "value that is used by another system" + +## Overrides have the same structure as [[constraint]], but supersede all +## [[constraint]] declarations from all projects. Only [[override]] from +## the current project's are applied. +## +## Overrides are a sledgehammer. Use them only as a last resort. +# [[override]] +## Required: the root import path of the project being constrained. +# name = "github.com/user/project" +# +## Optional: specifying a version constraint override will cause all other +## constraints on this project to be ignored; only the overridden constraint +## need be satisfied. +## Again, only one of "branch", "version" or "revision" can be specified. +# version = "1.0.0" +# branch = "master" +# revision = "abc123" +# +## Optional: specifying an alternate source location as an override will +## enforce that the alternate location is used for that project, regardless of +## what source location any dependent projects specify. +# source = "https://github.com/myfork/package.git" + + + +[[constraint]] + branch = "master" + name = "github.com/BurntSushi/toml" + +[[constraint]] + branch = "master" + name = "github.com/Joker/jade" + +[[constraint]] + branch = "master" + name = "github.com/Unknwon/i18n" + +[[constraint]] + branch = "master" + name = "github.com/aymerick/raymond" + +[[constraint]] + name = "github.com/davecgh/go-spew" + version = "1.1.0" + +[[constraint]] + branch = "master" + name = "github.com/eknkc/amber" + +[[constraint]] + branch = "master" + name = "github.com/esemplastic/unis" + +[[constraint]] + branch = "master" + name = "github.com/flosch/pongo2" + +[[constraint]] + name = "github.com/garyburd/redigo" + version = "1.1.0" + +[[constraint]] + branch = "master" + name = "github.com/gavv/monotime" + +[[constraint]] + branch = "master" + name = "github.com/getlantern/systray" + +[[constraint]] + name = "github.com/gorilla/websocket" + version = "1.1.0" + +[[constraint]] + name = "github.com/imdario/mergo" + version = "0.2.2" + +[[constraint]] + branch = "master" + name = "github.com/iris-contrib/httpexpect" + +[[constraint]] + branch = "master" + name = "github.com/klauspost/compress" + +[[constraint]] + branch = "master" + name = "github.com/microcosm-cc/bluemonday" + +[[constraint]] + branch = "master" + name = "github.com/monoculum/formam" + +[[constraint]] + branch = "master" + name = "github.com/moul/http2curl" + +[[constraint]] + name = "github.com/pmezard/go-difflib" + version = "1.0.0" + +[[constraint]] + branch = "master" + name = "github.com/russross/blackfriday" + +[[constraint]] + branch = "master" + name = "github.com/satori/go.uuid" + +[[constraint]] + branch = "master" + name = "github.com/valyala/bytebufferpool" + +[[constraint]] + branch = "master" + name = "github.com/xeipuuv/gojsonschema" + +[[constraint]] + branch = "master" + name = "github.com/yalp/jsonpath" + +[[constraint]] + name = "github.com/yudai/gojsondiff" + version = "1.0.0" + +[[constraint]] + branch = "master" + name = "golang.org/x/crypto" + +[[constraint]] + branch = "master" + name = "golang.org/x/text" + +[[constraint]] + branch = "v2" + name = "gopkg.in/yaml.v2" diff --git a/HISTORY.md b/HISTORY.md index a30b6f4d..51934559 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,1391 +1,122 @@ -# Changelog +# FAQ -**How to upgrade**: Open your command-line and execute this command: `go get -u gopkg.in/kataras/iris.v6`. - -## Looking for free support? +### Looking for free support? http://support.iris-go.com + https://kataras.rocket.chat/channel/iris -## Sa, 03 June 2017 +### Looking for previous versions? -New version 7 released. + https://github.com/kataras/iris#version -Navigate to the [master](https://github.com/kataras/iris/tree/master) branch for more. -This version, v6, will work "forever" by-design, it uses the vendor directory feature, so you get truly reproducible builds, as this method guards against upstream renames and delete. +### Should I upgrade my Iris? -Developers are not forced to upgrade to the version 7 if they don't really need it. +Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready. +> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes. + +**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`. +For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework). -## v5 -> v6 - -Users already notified for some breaking-changes, this section will help you -to adapt the new changes to your application, it contains an overview of the new features too. - - -- **FIX**: Upgrade the [httpcache](https://github.com/geekypanda/httpcache) vendor. As requested [here](http://support.iris-go.com/d/44-upgrade-httpcache-module). - - -- **View**: Provide an easier method on the community's question about "injecting" additional data outside of the route's main handler which calls the .Render, via middleware. - - As discussed [above](http://support.iris-go.com/d/27-using-middleware-to-inject-properties-for-templates). - - Click [here](https://github.com/kataras/iris/tree/v6/_examples/intermediate/view/context-view-data) for an example. - -_Update: 18 March 2017_ - -- **Sessions**: Enchance the community's feature request about custom encode and decode methods for the cookie value(sessionid) as requested [here](http://support.iris-go.com/d/29-mark-cookie-for-session-as-secure). - -_Update: 12 March 2017_ - -- Enhance Custom http errors with gzip and static files handler, as requested/reported [here](http://support.iris-go.com/d/17-fallback-handler-for-non-matched-routes). -- Enhance per-party custom http errors (now it works on any wildcard path too). -- Add a third parameter on `app.OnError(...)` for custom http errors with regexp validation, see [status_test.go](https://github.com/kataras/iris/blob/v6/status_test.go) for an example. -- Add a `context.ParamIntWildcard(...)` to skip the first slash, useful for wildcarded paths' parameters. - -- Shutdown with `app.Shutdown(context.Context) error`, no need for any third-parties, with `EventPolicy.Interrupted` and Go's 1.8 Gracefully Shutdown feature you're ready to go! -- HTTP/2 Go 1.8 `context.Push(target string, opts *http.PushOptions) error` is supported, example can be found [here](https://github.com/kataras/iris/blob/v6/adaptors/websocket/_examples/websocket_secure/main.go) - -- Router (two lines to add, new features) -- Template engines (two lines to add, same features as before, except their easier configuration) -- Basic middleware, that have been written by me, are transfared to the main repository[/middleware](https://github.com/kataras/iris/tree/v6/middleware) with a lot of improvements to the `recover middleware` (see the next) -- `func(http.ResponseWriter, r *http.Request, next http.HandlerFunc)` signature is fully compatible using `iris.ToHandler` helper wrapper func, without any need of custom boilerplate code. So all net/http middleware out there are supported, no need to re-invert the world here, search to the internet and you'll find a suitable to your case. - -- Load Configuration from an external file, yaml and toml: - - - [yaml-based](http://www.yaml.org/) configuration file using the `iris.YAML` function: `app := iris.New(iris.YAML("myconfiguration.yaml"))` - - [toml-based](https://github.com/toml-lang/toml) configuration file using the `iris.TOML` function: `app := iris.New(iris.TOML("myconfiguration.toml"))` - - -- Add `.Regex` middleware which does path validation using the `regexp` package, i.e `.Regex("param", "[0-9]+$")`. Useful for routers that don't support regex route path validation out-of-the-box. - -- Websocket additions: `c.Context() *iris.Context`, `ws.GetConnectionsByRoom("room name") []websocket.Connection`, `c.OnLeave(func(roomName string){})`, -```go - // SetValue sets a key-value pair on the connection's mem store. - c.SetValue(key string, value interface{}) - // GetValue gets a value by its key from the connection's mem store. - c.GetValue(key string) interface{} - // GetValueArrString gets a value as []string by its key from the connection's mem store. - c.GetValueArrString(key string) []string - // GetValueString gets a value as string by its key from the connection's mem store. - c.GetValueString(key string) string - // GetValueInt gets a value as integer by its key from the connection's mem store. - c.GetValueInt(key string) int - -``` -[examples here](https://github.com/kataras/iris/blob/v6/adaptors/websocket/_examples). - -Fixes: - -- Websocket improvements and fix errors when using custom golang client -- Sessions performance improvements -- Fix cors by using `rs/cors` and add a new adaptor to be able to wrap the entire router -- Fix and improve oauth/oauth2 plugin(now adaptor) -- Improve and fix recover middleware -- Fix typescript compiler and hot-reloader plugin(now adaptor) -- Fix and improve the cloud-editor `alm/alm-tools` plugin(now adaptor) -- Fix gorillamux serve static files (custom routers are supported with a workaround, not a complete solution as they are now) -- Fix `iris run main.go` app reload while user saved the file from gogland -- Fix [StaticEmbedded doesn't works on root "/"](https://github.com/kataras/iris/issues/633) - -Changes: - -- `context.TemplateString` replaced with `app.Render(w io.Writer, name string, bind interface{}, options ...map[string]interface{}) error)` which gives you more functionality. - -```go -import "bytes" -// .... -app := iris.New() -// .... - -buff := &bytes.Buffer{} -app.Render(buff, "my_template.html", nil) -// buff.String() is the template parser's result, use that string to send a rich-text e-mail based on a template. -``` - -```go -// you can take the app(*Framework instance) via *Context.Framework() too: - -app.Get("/send_mail", func(ctx *iris.Context){ - buff := &bytes.Buffer{} - ctx.Framework().Render(buff, "my_template.html", nil) - // ... -}) - -``` -- `.Close() error` replaced with gracefully `.Shutdown(context.Context) error` -- Remove all the package-level functions and variables for a default `*iris.Framework, iris.Default` -- Remove `.API`, use `iris.Handle/.HandleFunc/.Get/.Post/.Put/.Delete/.Trace/.Options/.Use/.UseFunc/.UseGlobal/.Party/` instead -- Remove `.Logger`, `.Config.IsDevelopment`, `.Config.LoggerOut`, `.Config.LoggerPrefix` you can adapt a logger which will log to each log message mode by `app.Adapt(iris.DevLogger())` or adapt a new one, it's just a `func(mode iris.LogMode, message string)`. -- Remove `.Config.DisableTemplateEngines`, are disabled by-default, you have to `.Adapt` a view engine by yourself -- Remove `context.RenderTemplateSource` you should make a new template file and use the `iris.Render` to specify an `io.Writer` like `bytes.Buffer` -- Remove `plugins`, replaced with more pluggable echosystem that I designed from zero on this release, named `Policy` [Adaptors](https://github.com/kataras/iris/tree/v6/adaptors) (all plugins have been converted, fixed and improvement, except the iriscontrol). -- `context.Log(string,...interface{})` -> `context.Log(iris.LogMode, string)` -- Remove `.Config.DisableBanner`, now it's controlled by `app.Adapt(iris.LoggerPolicy(func(mode iris.LogMode, msg string)))` -- Remove `.Config.Websocket` , replaced with the `kataras/iris/adaptors/websocket.Config` adaptor. - -- https://github.com/iris-contrib/plugin -> https://github.com/iris-contrib/adaptors - -- `import "github.com/iris-contrib/middleware/basicauth"` -> `import "gopkg.in/kataras/iris.v6/middleware/basicauth"` -- `import "github.com/iris-contrib/middleware/i18n"` -> `import "gopkg.in/kataras/iris.v6/middleware/i18n"` -- `import "github.com/iris-contrib/middleware/logger"` -> `import "gopkg.in/kataras/iris.v6/middleware/logger"` -- `import "github.com/iris-contrib/middleware/recovery"` -> `import "gopkg.in/kataras/iris.v6/middleware/recover"` - - -- `import "github.com/iris-contrib/plugin/typescript"` -> `import "gopkg.in/kataras/iris.v6/adaptors/typescript"` -- `import "github.com/iris-contrib/plugin/editor"` -> `import "gopkg.in/kataras/iris.v6/adaptors/typescript/editor"` -- `import "github.com/iris-contrib/plugin/cors"` -> `import "gopkg.in/kataras/iris.v6/adaptors/cors"` -- `import "github.com/iris-contrib/plugin/gorillamux"` -> `import "gopkg.in/kataras/iris.v6/adaptors/gorillamux"` -- `import github.com/iris-contrib/plugin/oauth"` -> `import "github.com/iris-contrib/adaptors/oauth"` - - -- `import "github.com/kataras/go-template/html"` -> `import "gopkg.in/kataras/iris.v6/adaptors/view"` -- `import "github.com/kataras/go-template/django"` -> `import "gopkg.in/kataras/iris.v6/adaptors/view"` -- `import "github.com/kataras/go-template/pug"` -> `import "gopkg.in/kataras/iris.v6/adaptors/view"` -- `import "github.com/kataras/go-template/handlebars"` -> `import "gopkg.in/kataras/iris.v6/adaptors/view"` -- `import "github.com/kataras/go-template/amber"` -> `import "gopkg.in/kataras/iris.v6/adaptors/view"` - -**Read more below** for the lines you have to change. Package-level removal is critical, you will have build-time errors. Router(less) is MUST, otherwise your app will fatal with a detailed error message. - -> If I missed something please [chat](https://kataras.rocket.chat/channel/iris). - - -### Router(less) - -**Iris server does not contain a default router anymore**, yes your eyes are ok. - -This decision came up because of your requests of using other routers than the iris' defaulted. -At the past I gave you many workarounds, but they are just workarounds, not a complete solution. - -**Don't worry:** - -- you have to add only two lines, one is the `import path` and another is the `.Adapt`, after the `iris.New()`, so it can be tolerated. -- you are able to use all iris' features as you used before, **the API for routing has not been changed**. - -Two routers available to use, today: - - -- [httprouter](https://github.com/kataras/iris/tree/v6/adaptors/httprouter), the old defaulted. A router that can be adapted, it's a custom version of https://github.comjulienschmidt/httprouter which is edited to support iris' subdomains, reverse routing, custom http errors and a lot features, it should be a bit faster than the original too because of iris' Context. It uses `/mypath/:firstParameter/path/:secondParameter` and `/mypath/*wildcardParamName` . - - -Example: - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" // <---- NEW -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(httprouter.New()) // <---- NEW - - - app.OnError(iris.StatusNotFound, func(ctx *iris.Context){ - ctx.HTML(iris.StatusNotFound, "

custom http error page

") - }) - - - app.Get("/healthcheck", h) - - gamesMiddleware := func(ctx *iris.Context) { - println(ctx.Method() + ": " + ctx.Path()) - ctx.Next() - } - - games:= app.Party("/games", gamesMiddleware) - { // braces are optional of course, it's just a style of code - games.Get("/:gameID/clans", h) - games.Get("/:gameID/clans/clan/:publicID", h) - games.Get("/:gameID/clans/search", h) - - games.Put("/:gameID/players/:publicID", h) - games.Put("/:gameID/clans/clan/:publicID", h) - - games.Post("/:gameID/clans", h) - games.Post("/:gameID/players", h) - games.Post("/:gameID/clans/:publicID/leave", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/application", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/application/:action", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/invitation", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/invitation/:action", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/delete", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/promote", h) - games.Post("/:gameID/clans/:clanPublicID/memberships/demote", h) - } - - app.Get("/anything/*anythingparameter", func(ctx *iris.Context){ - s := ctx.Param("anythingparameter") - ctx.Writef("The path after /anything is: %s",s) - }) - - app.Listen(":80") - - /* - gameID = 1 - publicID = 2 - clanPublicID = 22 - action = 3 - - GET - http://localhost/healthcheck - http://localhost/games/1/clans - http://localhost/games/1/clans/clan/2 - http://localhost/games/1/clans/search - - PUT - http://localhost/games/1/players/2 - http://localhost/games/1/clans/clan/2 - - POST - http://localhost/games/1/clans - http://localhost/games/1/players - http://localhost/games/1/clans/2/leave - http://localhost/games/1/clans/22/memberships/application -> 494 - http://localhost/games/1/clans/22/memberships/application/3- > 404 - http://localhost/games/1/clans/22/memberships/invitation - http://localhost/games/1/clans/22/memberships/invitation/3 - http://localhost/games/1/clans/2/memberships/delete - http://localhost/games/1/clans/22/memberships/promote - http://localhost/games/1/clans/22/memberships/demote - - */ -} - -func h(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "

Path

"+ctx.Path()) -} - -``` - -- [gorillamux](https://github.com/kataras/iris/tree/v6/adaptors/gorillamux), a router that can be adapted, it's the https://github.com/gorilla/mux which supports subdomains, custom http errors, reverse routing, pattern matching via regex and the rest of the iris' features. - - -Example: - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/gorillamux" // <---- NEW -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(gorillamux.New()) // <---- NEW - - - app.OnError(iris.StatusNotFound, func(ctx *iris.Context){ - ctx.HTML(iris.StatusNotFound, "

custom http error page

") - }) - - - app.Get("/healthcheck", h) - - gamesMiddleware := func(ctx *iris.Context) { - println(ctx.Method() + ": " + ctx.Path()) - ctx.Next() - } - - games:= app.Party("/games", gamesMiddleware) - { // braces are optional of course, it's just a style of code - games.Get("/{gameID:[0-9]+}/clans", h) - games.Get("/{gameID:[0-9]+}/clans/clan/{publicID:[0-9]+}", h) - games.Get("/{gameID:[0-9]+}/clans/search", h) - - games.Put("/{gameID:[0-9]+}/players/{publicID:[0-9]+}", h) - games.Put("/{gameID:[0-9]+}/clans/clan/{publicID:[0-9]+}", h) - - games.Post("/{gameID:[0-9]+}/clans", h) - games.Post("/{gameID:[0-9]+}/players", h) - games.Post("/{gameID:[0-9]+}/clans/{publicID:[0-9]+}/leave", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/application", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/application/:action", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/invitation", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/invitation/:action", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/delete", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/promote", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/demote", h) - } - - app.Get("/anything/{anythingparameter:.*}", func(ctx *iris.Context){ - s := ctx.Param("anythingparameter") - ctx.Writef("The path after /anything is: %s",s) - }) - - app.Listen(":80") - - /* - gameID = 1 - publicID = 2 - clanPublicID = 22 - action = 3 - - GET - http://localhost/healthcheck - http://localhost/games/1/clans - http://localhost/games/1/clans/clan/2 - http://localhost/games/1/clans/search - - PUT - http://localhost/games/1/players/2 - http://localhost/games/1/clans/clan/2 - - POST - http://localhost/games/1/clans - http://localhost/games/1/players - http://localhost/games/1/clans/2/leave - http://localhost/games/1/clans/22/memberships/application -> 494 - http://localhost/games/1/clans/22/memberships/application/3- > 404 - http://localhost/games/1/clans/22/memberships/invitation - http://localhost/games/1/clans/22/memberships/invitation/3 - http://localhost/games/1/clans/2/memberships/delete - http://localhost/games/1/clans/22/memberships/promote - http://localhost/games/1/clans/22/memberships/demote - - */ -} - -func h(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "

Path

"+ctx.Path()) -} - -``` - -**No changes whatever router you use**, only the `path` is changed(otherwise it doesn't make sense to support more than one router). -At the `gorillamux`'s path example we get pattern matching using regexp, at the other hand `httprouter` doesn't provides path validations -but it provides parameter and wildcard parameters too, it's also a lot faster than gorillamux. - -Original Gorilla Mux made my life easier when I had to adapt the reverse routing and subdomains features, it has got these features by its own too, so it was easy. - -Original Httprouter doesn't supports subdomains, multiple paths on different methods, reverse routing, custom http errors, I had to implement -all of them by myself and after adapt them using the policies, it was a bit painful but this is my job. Result: It runs blazy-fast! - - -As we said, all iris' features works as before even if you are able to adapt any custom router. Template funcs that were relative-closed to reverse router, like `{{ url }} and {{ urlpath }}`, works as before too, no change for your app's side need. - - -> I would love to see more routers (as more as they can provide different `path declaration` features) from the community, create an adaptor for an iris' router and I will share your repository to the rest of the users! - - -Adaptors are located [there](https://github.com/kataras/iris/tree/v6/adaptors). - -### View engine (5 template engine adaptors) - -At the past, If no template engine was used then iris selected the [html standard](https://github.com/kataras/go-template/tree/master/html). - -**Now, iris doesn't defaults any template engine** (also the `.Config.DisableTemplateEngines` has been removed, it has no use anymore). - -So, again you have to do two changes, the `import path` and the `.Adapt`. - -**Template files are no need to change, the template engines does the same exactly things as before** - - -All of these **five template engines** have common features with common API, like Layout, Template Funcs, Party-specific layout, partial rendering and more. - - **the standard html**, based on [go-template/html](https://github.com/kataras/go-template/tree/master/html), its template parser is the [html/template](https://golang.org/pkg/html/template/). - - - **django**, based on [go-template/django](https://github.com/kataras/go-template/tree/master/django), its template parser is the [pongo2](https://github.com/flosch/pongo2) - - - **pug**, based on [go-template/pug](https://github.com/kataras/go-template/tree/master/pug), its template parser is the [jade](https://github.com/Joker/jade) - - - **handlebars**, based on [go-template/handlebars](https://github.com/kataras/go-template/tree/master/handlebars), its template parser is the [raymond](https://github.com/aymerick/raymond) - - - **amber**, based on [go-template/amber](https://github.com/kataras/go-template/tree/master/amber), its template parser is the [amber](https://github.com/eknkc/amber). - -Each of the template engines has different options, view adaptors are located [here](https://github.com/kataras/iris/tree/v6/adaptors/view). - - -Example: - - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/gorillamux" // <--- NEW (previous section) - "gopkg.in/kataras/iris.v6/adaptors/view" // <--- NEW it contains all the template engines -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(gorillamux.New()) // <--- NEW (previous section) - - // - standard html | view.HTML(...) - // - django | view.Django(...) - // - pug(jade) | view.Pug(...) - // - handlebars | view.Handlebars(...) - // - amber | view.Amber(...) - app.Adapt(view.HTML("./templates", ".html").Reload(true)) // <---- NEW (set .Reload to true when you're in dev mode.) - - // default template funcs: - // - // - {{ url "mynamedroute" "pathParameter_ifneeded"} } - // - {{ urlpath "mynamedroute" "pathParameter_ifneeded" }} - // - {{ render "header.html" }} - // - {{ render_r "header.html" }} // partial relative path to current page - // - {{ yield }} - // - {{ current }} - // - // to adapt custom funcs, use: - app.Adapt(iris.TemplateFuncsPolicy{"myfunc": func(s string) string { - return "hi "+s - }}) // usage inside template: {{ hi "kataras"}} - - app.Get("/hi", func(ctx *iris.Context) { - ctx.MustRender( - "hi.html", // the file name of the template relative to the './templates' - iris.Map{"Name": "Iris"}, // the .Name inside the ./templates/hi.html - iris.Map{"gzip": false}, // enable gzip for big files - ) - - }) - - // http://127.0.0.1:8080/hi - app.Listen(":8080") -} - - -``` - -`.UseTemplate` have been removed and replaced with the `.Adapt` which is using `iris.RenderPolicy` and `iris.TemplateFuncsPolicy` -to adapt the behavior of the custom template engines. - -**BEFORE** -```go -import "github.com/kataras/go-template/django" -// ... -app := iris.New() -app.UseTemplate(django.New()).Directory("./templates", ".html")/*.Binary(...)*/) -``` - -**AFTER** -```go -import ""gopkg.in/kataras/iris.v6/adaptors/view" -// ... -app := iris.New() -app.Adapt(view.Django("./templates",".htmll")/*.Binary(...)*/) -``` - -The rest remains the same. Don't forget the real changes were `only import path and .Adapt(imported)`, at general when you see an 'adaptor' these two declarations should happen to your code. - - - -### Package-level functions and variables for `iris.Default` have been removed. - -The form of variable use for an Iris *Framework remains as it was: -```go -app := iris.New() -app.$FUNCTION/$VARIABLE -``` - - -> When I refer to `iris.$FUNCTION/$VARIABLE` it means `iris.Handle/.HandleFunc/.Get/.Post/.Put/.Delete/.Trace/.Options/.Use/.UseFunc/.UseGlobal/.Party/.Set/.Config` -and the rest of the package-level functions referred to the `iris.Default` variable. - -**BEFORE** - -```go -iris.Config.FireMethodNotAllowed = true -iris.Set(OptionDisableBodyConsumptionOnUnmarshal(true)) -``` - -```go -iris.Get("/", func(ctx *iris.Context){ - -}) - -iris.ListenLETSENCRYPT(":8080") -``` - -**AFTER** - - -```go -app := iris.New() -app.Config.FireMethodNotAllowed = true -// or iris.Default.Config.FireMethodNotAllowed = true and so on -app.Set(OptionDisableBodyConsumptionOnUnmarshal(true)) -// same as -// app := iris.New(iris.Configuration{FireMethodNotAllowed:true, DisableBodyConsumptionOnUnmarshal:true}) -``` - -```go -app := iris.New() -app.Get("/", func(ctx *iris.Context){ - -}) - -app.ListenLETSENCRYPT(":8080") -``` - -For those who had splitted the application in different packages they could do just that `iris.$FUNCTION/$VARIABLE` without the need -of import a singleton package which would initialize a new `App := iris.New()`. - -`Iris.Default` remains, so you can refer to that if you don't want to initialize a new `App := iris.New()` by your own. - -**BEFORE** - -```go -package controllers -import "github.com/kataras/iris" -func init(){ - iris.Get("/", func(ctx *iris.Context){ - - }) -} -``` - -```go -package main - -import ( - "github.com/kataras/iris" - _ "github.com/mypackage/controllers" -) - -func main(){ - iris.Listen(":8080") -} -``` - - -**AFTER** - -```go -package controllers - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func init(){ - iris.Default.Adapt(httprouter.New()) - iris.Default.Get("/", func(ctx *iris.Context){ - - }) -} -``` - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - _ "github.com/mypackage/controllers" -) - -func main(){ - iris.Default.Listen(":8080") -} -``` - -You got the point, let's continue to the next conversion. - -### Remove the slow .API | iris.API(...) / app := iris.New(); app.API(...) - - -The deprecated `.API` has been removed entirely, it should be removed after v5(look on the v5 history tag). - -At first I created that func in order to give newcovers a chance to be able to quick start a new `controller-like` -with one function, but that function was using generics at runtime and it was very slow compared to the -`iris.Handle/.HandleFunc/.Get/.Post/.Put/.Delete/.Trace/.Options/.Use/.UseFunc/.UseGlobal/.Party`. - -Also some users they used only `.API`, they didn't bother to 'learn' about the standard rest api functions -and their power(including per-route middleware, cors, recover and so on). So we had many unrelational questions about the `.API` func. - - -**BEFORE** - -```go -package main - -import ( - "github.com/kataras/iris" -) - -type UserAPI struct { - *iris.Context -} - -// GET /users -func (u UserAPI) Get() { - u.Writef("Get from /users") - // u.JSON(iris.StatusOK,myDb.AllUsers()) -} - -// GET /users/:param1 which its value passed to the id argument -func (u UserAPI) GetBy(id string) { // id equals to u.Param("param1") - u.Writef("Get from /users/%s", id) - // u.JSON(iris.StatusOK, myDb.GetUserById(id)) - -} - -// POST /users -func (u UserAPI) Post() { - name := u.FormValue("name") - // myDb.InsertUser(...) - println(string(name)) - println("Post from /users") -} - -// PUT /users/:param1 -func (u UserAPI) PutBy(id string) { - name := u.FormValue("name") // you can still use the whole Context's features! - // myDb.UpdateUser(...) - println(string(name)) - println("Put from /users/" + id) -} - -// DELETE /users/:param1 -func (u UserAPI) DeleteBy(id string) { - // myDb.DeleteUser(id) - println("Delete from /" + id) -} - -func main() { - - iris.API("/users", UserAPI{}) - iris.Listen(":8080") -} - -``` - - -**AFTER** - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/gorillamux" -) - -func GetAllUsersHandler(ctx *iris.Context) { - ctx.Writef("Get from /users") - // ctx.JSON(iris.StatusOK,myDb.AllUsers()) -} - -func GetUserByIdHandler(ctx *iris.Context) { - ctx.Writef("Get from /users/%s", - ctx.Param("id")) // or id, err := ctx.ParamInt("id") - // ctx.JSON(iris.StatusOK, myDb.GetUserById(id)) -} - -func InsertUserHandler(ctx *iris.Context){ - name := ctx.FormValue("name") - // myDb.InsertUser(...) - println(string(name)) - println("Post from /users") -} - -func UpdateUserHandler(ctx *iris.Context) { - name := ctx.FormValue("name") - // myDb.UpdateUser(...) - println(string(name)) - println("Put from /users/" + ctx.Param("id")) -} - -func DeleteUserById(id string) { - // myDb.DeleteUser(id) - println("Delete from /" + ctx.param("id")) -} - -func main() { - app := iris.New() - app.Adapt(gorillamux.New()) - - // create a new router targeted for "/users" path prefix - // you can learn more about Parties on the examples and book too - // they can share middleware, template layout and more. - userRoutes := app.Party("users") - - // GET http://localhost:8080/users/ and /users - userRoutes.Get("/", GetAllUsersHandler) - - // GET http://localhost:8080/users/:id - userRoutes.Get("/:id", GetUserByIdHandler) - // POST http://localhost:8080/users - userRoutes.Post("/", InsertUserHandler) - - // PUT http://localhost:8080/users/:id - userRoutes.Put("/:id", UpdateUserHandler) - - // DELETE http://localhost:8080/users/:id - userRoutes.Delete("/:id", DeleteUserById) - - app.Listen(":8080") -} - -``` - -### Old Plugins and the new `.Adapt` Policies - -A lot of changes to old -so-called Plugins and many features have been adopted to this new ecosystem. - - -First of all plugins renamed to `policies with adaptors which, adaptors, adapts the policies to the framework` -(it is not just a simple rename of the word, it's a new concept). - - -Policies are declared inside Framework, they are implemented outside of the Framework and they are adapted to Framework by a user call. - -Policy adaptors are just like a plugins but they have to implement a specific action/behavior to a specific policy type(or more than one at the time). - -The old plugins are fired 'when something happens do that' (ex: PreBuild,PostBuild,PreListen and so on) this behavior is the new `EventPolicy` -which has **4 main flow events** with their callbacks been wrapped, so you can use more than EventPolicy (most of the policies works this way). - -```go -type ( - // EventListener is the signature for type of func(*Framework), - // which is used to register events inside an EventPolicy. - // - // Keep note that, inside the policy this is a wrapper - // in order to register more than one listener without the need of slice. - EventListener func(*Framework) - - // EventPolicy contains the available Framework's flow event callbacks. - // Available events: - // - Boot - // - Build - // - Interrupted - // - Recovery - EventPolicy struct { - // Boot with a listener type of EventListener. - // Fires when '.Boot' is called (by .Serve functions or manually), - // before the Build of the components and the Listen, - // after VHost and VSCheme configuration has been setted. - Boot EventListener - // Before Listen, after Boot - Build EventListener - // Interrupted with a listener type of EventListener. - // Fires after the terminal is interrupted manually by Ctrl/Cmd + C - // which should be used to release external resources. - // Iris will close and os.Exit at the end of custom interrupted events. - // If you want to prevent the default behavior just block on the custom Interrupted event. - Interrupted EventListener - // Recovery with a listener type of func(*Framework,error). - // Fires when an unexpected error(panic) is happening at runtime, - // while the server's net.Listener accepting requests - // or when a '.Must' call contains a filled error. - // Used to release external resources and '.Close' the server. - // Only one type of this callback is allowed. - // - // If not empty then the Framework will skip its internal - // server's '.Close' and panic to its '.Logger' and execute that callback instaed. - // Differences from Interrupted: - // 1. Fires on unexpected errors - // 2. Only one listener is allowed. - Recovery func(*Framework, error) - } -) -``` - -**A quick overview on how they can be adapted** to an iris *Framework (iris.New()'s result). -Let's adapt `EventPolicy`: - -```go -app := iris.New() - -evts := iris.EventPolicy{ - // we ommit the *Framework's variable name because we have already the 'app' - // if we were on different file with no access to the 'app' then the varialbe name will be useful. - Boot: func(*Framework){ - app.Log("Here you can change any field and configuration for iris before being used - also you can adapt more policies that should be used to the next step which is the Build and Listen, - only the app.Config.VHost and app.Config.VScheme have been setted here, but you can change them too\n") - }, - Build: func(*Framework){ - app.Log("Here all configuration and all app' fields and features have been builded, here you are ready to call - anything (you shouldn't change fields and configuration here)\n") - }, -} -// Adapt the EventPolicy 'evts' to the Framework -app.Adapt(evts) - -// let's register one more -app.Adapt(iris.EventPolicy{ - Boot: func(*Framework){ - app.Log("the second log message from .Boot!\n") -}}) - -// you can also adapt multiple and different(or same) types of policies in the same call -// using: app.Adapt(iris.EventPolicy{...}, iris.LoggerPolicy(...), iris.RouterWrapperPolicy(...)) - -// starts the server, executes the Boot -> Build... -app.Adapt(httprouter.New()) // read below for this line -app.Listen(":8080") -``` - - - -This pattern allows us to be very pluggable and add features that the *Framework itself doesn't knows, -it knows only the main policies which implement but their features are our(as users) business. - - -We have 8 policies, so far, and some of them have 'subpolicies' (the RouterReversionPolicy for example). - -- LoggerPolicy -- EventPolicy - - Boot - - Build - - Interrupted - - Recover -- RouterReversionPolicy - - StaticPath - - WildcardPath - - Param - - URLPath -- RouterBuilderPolicy -- RouterWrapperPolicy -- RenderPolicy -- TemplateFuncsPolicy -- SessionsPolicy - - -**Details** of these can be found at [policy.go](https://github.com/kataras/iris/blob/v6/policy.go). - -The **Community**'s adaptors are [here](https://github.com/iris-contrib/adaptors). - -**Iris' Built'n Adaptors** for these policies can be found at [/adaptors folder](https://github.com/kataras/iris/tree/v6/adaptors). - -The folder contains: - -- cors, a cors (router) wrapper based on `rs/cors`. -It's a `RouterWrapperPolicy` - -- gorillamux, a router that can be adapted, it's the `gorilla/mux` which supports subdomains, custom http errors, reverse routing, pattern matching. -It's a compination of`EventPolicy`, `RouterReversionPolicy with StaticPath, WildcardPath, URLPath, RouteContextLinker` and the `RouterBuilderPolicy`. - -- httprouter, a router that can be adapted, it's a custom version of `julienschmidt/httprouter` which is edited to support iris' subdomains, reverse routing, custom http errors and a lot features, it should be a bit faster than the original too. -It's a compination of`EventPolicy`, `RouterReversionPolicy with StaticPath, WildcardPath, URLPath, RouteContextLinker` and the `RouterBuilderPolicy`. - - -- typescript and cloud editor, contains the typescript compiler with hot reload feature and a typescript cloud editor ([alm-tools](https://github.com/alm-tools/alm)), it's an `EventPolicy` - -- view, contains 5 template engines based on the `kataras/go-template`. -All of these have common features with common API, like Layout, Template Funcs, Party-specific layout, partial rendering and more. -It's a `RenderPolicy` with a compinaton of `EventPolicy` and use of `TemplateFuncsPolicy`. - - the standard html - - pug(jade) - - django(pongo2) - - handlebars - - amber. - - - - - -#### Note -Go v1.8 introduced a new plugin system with `.so` files, users should not be confused with old iris' plugins and new adaptors. -It is not ready for all operating systems(yet) when it will be ready, Iris will take leverage of this Golang's feature. - - -### http.Handler and third-party middleware - -We were compatible before this version but if a third-party middleware had the form of: -`func(http.ResponseWriter, *http.Request, http.HandlerFunc)`you were responsible of make a wrapper -which would return an `iris.Handler/HandlerFunc`. - -Now you're able to pass an `func(http.ResponseWriter, *http.Request, http.HandlerFunc)` third-party net/http middleware(Chain-of-responsibility pattern) using the `iris.ToHandler` wrapper func without any other custom boilerplate. - -Example: - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/gorillamux" - "github.com/rs/cors" -) - -// myCors returns a new cors middleware -// with the provided options. -myCors := func(opts cors.Options) iris.HandlerFunc { - handlerWithNext := cors.New(opts).ServeHTTP - return iris.ToHandler(handlerWithNext) -} - -func main(){ - app := iris.New() - app.Adapt(httprouter.New()) - - app.Post("/user", myCors(cors.Options{}), func(ctx *iris.Context){ - // .... - }) - - app.Listen(":8080") -} - -``` - -- Irrelative info but this is the best place to put it: `iris/app.AcquireCtx/.ReleaseCtx` replaced to: `app.Context.Acquire/.Release/.Run`. - - - -### iris cmd - -- FIX: [iris run main.go](https://github.com/kataras/iris/tree/v6/iris#run) not reloading when file changes maden by some of the IDEs, -because they do override the operating system's fs signals. The majority of -editors worked before but I couldn't let some developers without support. - - -### Sessions - - -Sessions manager is also an Adaptor now, `iris.SessionsPolicy`. -So far we used the `kataras/go-sessions`, you could always use other session manager ofcourse but you would lose the `context.Session()` -and its returning value, the `iris.Session` now. - -`SessionsPolicy` gives the developers the opportunity to adapt any, -compatible with a particular simple interface(Start and Destroy methods), third-party sessions managers. - -- The API for sessions inside context is the same, no matter what session manager you wanna to adapt. -- The API for sessions inside context didn't changed, it's the same as you knew it. - -- Iris, of course, has built'n `SessionsPolicy` adaptor(the kataras/go-sessions: edited to remove fasthttp dependencies). - - Sessions manager works even faster now and a bug fixed for some browsers. - -- Functions like, adding a database or store(i.e: `UseDatabase`) depends on the session manager of your choice, -Iris doesn't requires these things -to adapt a package as a session manager. So `iris.UseDatabase` has been removed and depends on the `mySessions.UseDatabase` you 'll see below. - -- `iris.DestroySessionByID and iris.DestroyAllSessions` have been also removed, depends on the session manager of your choice, `mySessions.DestroyByID and mySessions.DestroyAll` should do the job now. - - -> Don't worry about forgetting to adapt any feature that you use inside Iris, Iris will print you a how-to-fix message at iris.DevMode log level. - - -**[Examples folder](https://github.com/kataras/iris/tree/v6/adaptors/sessions/_examples)** - - - -```go -package main - -import ( - "time" - - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/sessions" -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) // enable all (error) logs - app.Adapt(httprouter.New()) // select the httprouter as the servemux - - mySessions := sessions.New(sessions.Config{ - // Cookie string, the session's client cookie name, for example: "mysessionid" - // - // Defaults to "irissessionid" - Cookie: "mysessionid", - // base64 urlencoding, - // if you have strange name cookie name enable this - DecodeCookie: false, - // it's time.Duration, from the time cookie is created, how long it can be alive? - // 0 means no expire. - // -1 means expire when browser closes - // or set a value, like 2 hours: - Expires: time.Hour * 2, - // the length of the sessionid's cookie's value - CookieLength: 32, - // if you want to invalid cookies on different subdomains - // of the same host, then enable it - DisableSubdomainPersistence: false, - }) - - // OPTIONALLY: - // import "gopkg.in/kataras/iris.v6/adaptors/sessions/sessiondb/redis" - // or import "github.com/kataras/go-sessions/sessiondb/$any_available_community_database" - // mySessions.UseDatabase(redis.New(...)) - - app.Adapt(mySessions) // Adapt the session manager we just created. - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("You should navigate to the /set, /get, /delete, /clear,/destroy instead") - }) - app.Get("/set", func(ctx *iris.Context) { - - //set session values - ctx.Session().Set("name", "iris") - - //test if setted here - ctx.Writef("All ok session setted to: %s", ctx.Session().GetString("name")) - }) - - app.Get("/get", func(ctx *iris.Context) { - // get a specific key, as string, if no found returns just an empty string - name := ctx.Session().GetString("name") - - ctx.Writef("The name on the /set was: %s", name) - }) - - app.Get("/delete", func(ctx *iris.Context) { - // delete a specific key - ctx.Session().Delete("name") - }) - - app.Get("/clear", func(ctx *iris.Context) { - // removes all entries - ctx.Session().Clear() - }) - - app.Get("/destroy", func(ctx *iris.Context) { - - //destroy, removes the entire session and cookie - ctx.SessionDestroy() - msg := "You have to refresh the page to completely remove the session (browsers works this way, it's not iris-specific.)" - - ctx.Writef(msg) - ctx.Log(iris.DevMode, msg) - }) // Note about destroy: - // - // You can destroy a session outside of a handler too, using the: - // mySessions.DestroyByID - // mySessions.DestroyAll - - app.Listen(":8080") -} - -``` - -### Websockets - -There are many internal improvements to the websocket server, it -operates slighty faster to. - - -Websocket is an Adaptor too and you can edit more configuration fields than before. -No Write and Read timeout by default, you have to set the fields if you want to enable timeout. - -Below you'll see the before and the after, keep note that the static and templates didn't changed, so I am not putting the whole -html and javascript sources here, you can run the full examples from [here](https://github.com/kataras/iris/tree/v6/adaptors/websocket/_examples). - -**BEFORE:*** - -```go - -package main - -import ( - "fmt" // optional - - "github.com/kataras/iris" -) - -type clientPage struct { - Title string - Host string -} - -func main() { - iris.StaticWeb("/js", "./static/js") - - iris.Get("/", func(ctx *iris.Context) { - ctx.Render("client.html", clientPage{"Client Page", ctx.Host()}) - }) - - // the path which the websocket client should listen/registered to -> - iris.Config.Websocket.Endpoint = "/my_endpoint" - // by-default all origins are accepted, you can change this behavior by setting: - // iris.Config.Websocket.CheckOrigin - - var myChatRoom = "room1" - iris.Websocket.OnConnection(func(c iris.WebsocketConnection) { - // Request returns the (upgraded) *http.Request of this connection - // avoid using it, you normally don't need it, - // websocket has everything you need to authenticate the user BUT if it's necessary - // then you use it to receive user information, for example: from headers. - - // httpRequest := c.Request() - // fmt.Printf("Headers for the connection with ID: %s\n\n", c.ID()) - // for k, v := range httpRequest.Header { - // fmt.Printf("%s = '%s'\n", k, strings.Join(v, ", ")) - // } - - // join to a room (optional) - c.Join(myChatRoom) - - c.On("chat", func(message string) { - if message == "leave" { - c.Leave(myChatRoom) - c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.") - c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.") - return - } - // to all except this connection -> - // c.To(iris.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message) - // to all connected clients: c.To(iris.All) - - // to the client itself -> - //c.Emit("chat", "Message from myself: "+message) - - //send the message to the whole room, - //all connections are inside this room will receive this message - c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message) - }) - - // or create a new leave event - // c.On("leave", func() { - // c.Leave(myChatRoom) - // }) - - c.OnDisconnect(func() { - fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID()) - - }) - }) - - iris.Listen(":8080") -} - - - -``` - - -**AFTER** -```go -package main - -import ( - "fmt" // optional - - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/view" - "gopkg.in/kataras/iris.v6/adaptors/websocket" -) - -type clientPage struct { - Title string - Host string -} - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) // enable all (error) logs - app.Adapt(httprouter.New()) // select the httprouter as the servemux - app.Adapt(view.HTML("./templates", ".html")) // select the html engine to serve templates - - ws := websocket.New(websocket.Config{ - // the path which the websocket client should listen/registered to, - Endpoint: "/my_endpoint", - // the client-side javascript static file path - // which will be served by Iris. - // default is /iris-ws.js - // if you change that you have to change the bottom of templates/client.html - // script tag: - ClientSourcePath: "/iris-ws.js", - // - // Set the timeouts, 0 means no timeout - // websocket has more configuration, go to ../../config.go for more: - // WriteTimeout: 0, - // ReadTimeout: 0, - // by-default all origins are accepted, you can change this behavior by setting: - // CheckOrigin: (r *http.Request ) bool {}, - // - // - // IDGenerator used to create (and later on, set) - // an ID for each incoming websocket connections (clients). - // The request is an argument which you can use to generate the ID (from headers for example). - // If empty then the ID is generated by DefaultIDGenerator: randomString(64): - // IDGenerator func(ctx *iris.Context) string {}, - }) - - app.Adapt(ws) // adapt the websocket server, you can adapt more than one with different Endpoint - - app.StaticWeb("/js", "./static/js") // serve our custom javascript code - - app.Get("/", func(ctx *iris.Context) { - ctx.Render("client.html", clientPage{"Client Page", ctx.Host()}) - }) - - var myChatRoom = "room1" - - ws.OnConnection(func(c websocket.Connection) { - // Context returns the (upgraded) *iris.Context of this connection - // avoid using it, you normally don't need it, - // websocket has everything you need to authenticate the user BUT if it's necessary - // then you use it to receive user information, for example: from headers. - - // ctx := c.Context() - - // join to a room (optional) - c.Join(myChatRoom) - - c.On("chat", func(message string) { - if message == "leave" { - c.Leave(myChatRoom) - c.To(myChatRoom).Emit("chat", "Client with ID: "+c.ID()+" left from the room and cannot send or receive message to/from this room.") - c.Emit("chat", "You have left from the room: "+myChatRoom+" you cannot send or receive any messages from others inside that room.") - return - } - // to all except this connection -> - // c.To(websocket.Broadcast).Emit("chat", "Message from: "+c.ID()+"-> "+message) - // to all connected clients: c.To(websocket.All) - - // to the client itself -> - //c.Emit("chat", "Message from myself: "+message) - - //send the message to the whole room, - //all connections are inside this room will receive this message - c.To(myChatRoom).Emit("chat", "From: "+c.ID()+": "+message) - }) - - // or create a new leave event - // c.On("leave", func() { - // c.Leave(myChatRoom) - // }) - - c.OnDisconnect(func() { - fmt.Printf("Connection with ID: %s has been disconnected!\n", c.ID()) - }) - }) - - app.Listen(":8080") -} - -``` - - - - -If the iris' websocket feature does not cover your app's needs, you can simply use any other -library for websockets that you used to use, like the Golang's compatible to `socket.io`, simple example: - -```go -package main - -import ( - "log" - - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "github.com/googollee/go-socket.io" -) - -func main() { - app := iris.New() - app.Adapt(httprouter.New()) - server, err := socketio.NewServer(nil) - if err != nil { - log.Fatal(err) - } - server.On("connection", func(so socketio.Socket) { - log.Println("on connection") - so.Join("chat") - so.On("chat message", func(msg string) { - log.Println("emit:", so.Emit("chat message", msg)) - so.BroadcastTo("chat", "chat message", msg) - }) - so.On("disconnection", func() { - log.Println("on disconnect") - }) - }) - server.On("error", func(so socketio.Socket, err error) { - log.Println("error:", err) - }) - - app.Any("/socket.io", iris.ToHandler(server)) - - app.Listen(":5000") -} -``` - -### Typescript compiler and cloud-based editor - -The Typescript compiler adaptor(old 'plugin') has been fixed (it had an issue on new typescript versions). -Example can be bound [here](https://github.com/kataras/iris/tree/v6/adaptors/typescript/_example). - -The Cloud-based editor adaptor(old 'plugin') also fixed and improved to show debug messages to your iris' LoggerPolicy. -Example can be bound [here](https://github.com/kataras/iris/tree/v6/adaptors/typescript/editor/_example). - -Their import paths also changed as the rest of the old plugins from: https://github.com/iris-contrib/plugin to https://github.com/kataras/adaptors and https://github.com/iris-contrib/adaptors -I had them on iris-contrib because I thought that community would help but it didn't, no problem, they are at the same codebase now -which making things easier to debug for me. - - -### Oauth/OAuth2 -Fix the oauth/oauth2 adaptor (old 'plugin') . -Example can be found [here](https://github.com/iris-contrib/adaptors/tree/master/oauth/_example). - - -### CORS Middleware and the new Wrapper - -Lets speak about history of cors middleware, almost all the issues users reported to the iris-contrib/middleware repository -were relative to the CORS middleware, some users done it work some others don't... it was strange. Keep note that this was one of the two middleware that I didn't -wrote by myself, it was a PR by a member who wrote that middleware and after didn't answer on users' issues. - -Forget about it I removed it entirely and replaced with the `rs/cors`: we now use the https://github.com/rs/cors in two forms: - -First, you can use the original middlare that you can install by `go get -u github.com/rs/cors` -(You had already see its example on the net/http handlers and iris.ToHandler section) - -Can be registered globally or per-route but the `MethodsAllowed option doesn't works`. - -Example: - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/gorillamux" - "github.com/rs/cors" -) - -func main(){ - app := iris.New() - app.Adapt(httprouter.New()) // see below for that - corsMiddleware := iris.ToHandler(cors.Default().ServeHTTP) - app.Post("/user", corsMiddleware, func(ctx *iris.Context){ - // .... - }) - - app.Listen(":8080") -} -``` - -Secondly, probably the one which you will choose to use, is the `cors` Router Wrapper Adaptor. -It's already installed when you install iris because it's located at `kataras/iris/adaptors/cors`. - -This will wrap the entirely router so the whole of your app will be passing by the rules you setted up on its `cors.Options`. - -Again, it's functionality comes from the well-tested `rs/cors`, all known Options are working as expected. - -Example: - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/cors" -) - -func main(){ - app := iris.New() - app.Adapt(httprouter.New()) // see below for that - app.Adapt(cors.New(cors.Options{})) // or cors.Default() - - app.Post("/user", func(ctx *iris.Context){ - // .... - }) - - app.Listen(":8080") -} - -``` - -### FAQ -You know that you can always share your opinion and ask anything iris-relative with the rest of us, [here](https://kataras.rocket.chat/channel/iris). + +### About our new home page + http://iris-go.com + +Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.com has been upgraded and it's really awesome! + +[Santosh](https://github.com/santoshanand) is a freelancer, he has a great knowledge of nodejs and express js, Android, iOS, React Native, Vue.js etc, if you need a developer to find or create a solution for your problem or task, please contact with him. + + +The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please! + +# Sa, 03 June 2017 + +After 2+ months of hard work and collaborations, Iris [version 7](https://github.com/kataras/iris) was published earlier today. + +If you're new to Iris you don't have to read all these, just navigate to the [updated examples](https://github.com/kataras/iris/tree/master/_examples) and you should be fine:) + +Note that this section will not +cover the internal changes, the difference is so big that anybody can see them with a glimpse, even the code structure itself. + + +## Changes from [v6](https://github.com/kataras/iris/tree/v6) + +Vendoring /w update + +The previous vendor action for v6 was done by-hand, now I'm using the [go dep](https://github.com/golang/dep) tool, I had to do +some small steps: + +- remove files like testdata to reduce the folder size +- rollback some of the "golang/x/net/ipv4" and "ipv6" source files because they are downloaded to their latest versions +by go dep, but they had lines with the `typealias` feature, which is not ready by current golang version (it will be on August) +- fix "cannot use internal package" at golang/x/net/ipv4 and ipv6 packages + - rename the interal folder to was-internal, everywhere and fix its references. +- fix "main redeclared in this block" + - remove the examples folder from everywhere. + +The go dep tool does what is says, as expected, don't be afraid of it now. +I am totally recommending this tool for package authors, even if it's in its alpha state. +I remember when Iris was in its alpha state and it had 4k stars on its first weeks/or month and that helped me a lot to fix reported bugs by users and make the framework even better, so give love to go dep from today! + +General +- All `Listen` methods replaced with a single `Run` method, see [here](https://github.com/kataras/iris/tree/master/_examples/beginner/listening) +- Configuration, easier to modify the defaults, see [here](https://github.com/kataras/iris/tree/master/_examples/beginner/cofiguration) +- `HandlerFunc` removed, just `Handler` of `func(context.Context)` where context.Context derives from `import "github.com/kataras/iris/context"` (on August this import path will be optional) + - Simplify API, i.e instead of all these, `Handle,HandleFunc,Use,UseFunc,Done,DoneFunc,UseGlobal,UseGlobalFunc` use `Handle,Use,Done,UseGlobal`. +- Response time decreased even more (9-35%, depends on the application) +- The `Adaptors` idea replaced with a more structural design pattern, but you have to apply these changes: + - `app.Adapt(view.HTML/Pug/Amber/Django/Handlebars...)` -> `app.AttachView(view.HTML/Pug/Amber/Django/Handlebars...)` + - `app.Adapt(sessions.New(...))` -> `app.AttachSessionManager(sessions.New(...))` + - `app.Adapt(iris.LoggerPolicy(...))` -> `app.AttachLogger(io.Writer)` + - `app.Adapt(iris.RenderPolicy(...))` -> removed and replaced with the ability to replace the whole context with a custom one or override some methods of it, see below. + +Routing +- Remove of multiple routers, now we have the fresh Iris router which is based on top of the julien's [httprouter](https://github.com/julienschmidt/httprouter) +- Subdomains routing algorithm has been improved. +- Iris router is using a custom interpreter with parser and path evaluator to achieve the best expressiveness, with zero performance loss, you ever seen so far, i.e: + - `app.Get("/", "/users/{userid:int min(1)}", handler)`, + - `{username:string}` or just `{username}` + - `{asset:path}`, + - `{firstname:alphabetical}`, + - `{requestfile:file}` , + - `{mylowercaseParam regexp([a-z]+)}`. + - The previous syntax of `:param` and `*param` syntax still working as expected. Previous rules for paths confliction remain as they were. + - Also, path parameter names should be only alphabetical now, numbers and symbols are not allowed (for your own good, I have seen a lot the last year...). + +Click [here](https://github.com/kataras/iris/tree/master/_examples/beginner/routing) for details. +> It was my first attempt/experience on the interpreters field, so be good with it :) + +Context +- `iris.Context pointer` replaced with `context.Context interface` as we already mention + - in order to be able to use a custom context and/or catch lifetime like `BeginRequest` and `EndRequest` from context itself, see below +- `context.JSON, context.JSONP, context.XML, context.Markdown, context.HTML` work faster +- `context.Render("filename.ext", bindingViewData{}, options) ` -> `context.View("filename.ext")` + - `View` renders only templates, it will not try to search if you have a restful renderer adapted, because, now, you can do it via method overloading using a custom Context. + - Able to set `context.ViewData` and `context.ViewLayout` via middleware when executing a template. +- `context.SetStatusCode(statusCode)` -> `context.StatusCode(statusCode)` + - which is equivalent with the old `EmitError` too: + - if status code >=400 given can automatically fire a custom http error handler if response wasn't written already. + - `context.GetStatusCode` -> `context.GetStatusCode()`. + - `app.OnError` -> `app.OnErrorCode` + - Errors per party are removed by-default, you can just use one global error handler with logic like "if path starts with 'prefix' fire this error handler, else...". +- Easy way to change Iris' default `Context` with a custom one, see [here](https://github.com/kataras/iris/tree/master/_examples/intermediate/custom-context) +- `context.ResponseWriter().SetBeforeFlush(...)` works for Flush and HTTP/2 Push, respectfully +- Several improvements under the `Request transactions` +- Remember that you had to set a status code on each of the render-relative methods? Now it's not required, it just renders +with the status code that user gave with `context.StatusCode` or with `200 OK`, i.e: + -`context.JSON(iris.StatusOK, myJSON{})` -> `context.JSON(myJSON{})`. + - Each one of the context's render methods has optional per-call settings, + - **the new API is even more easier to read, understand and use.** + +Server +- Several enhancements for the typescript transpiler, view engine, websocket server and sessions manager +- Able to set custom underline *http.Server(s) with new Host (aka Server Supervisor) feature + - `Done` and `Err` channels to catch shutdown or any errors on custom hosts, + - Schedule custom tasks(with cancelation) when server is running, see [here](https://github.com/kataras/iris/tree/master/_examples/intermediate/graceful-shutdown) + - Taskbar Tray icon (disabled by default) + - Linux users have to download manually some dependencies if `app.WithTrayIcon` is passed on `app.Run`'s as second argument, the logger will print how. +- Interrupt handler task for gracefully shutdown (when `CTRL/CMD+C`) are enabled by-default, you can disable its via configuration: `app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler)` + +Future plans +- Future Go1.9's [ServeTLS](https://go-review.googlesource.com/c/38114/2/src/net/http/server.go) is ready when 1.9 released +- Future Go1.9's typealias feature is ready when 1.9 released, i.e `context.Context` -> `iris.Context` just one import path instead of todays' two. \ No newline at end of file diff --git a/LICENSE b/LICENSE index 2935ad5d..05050318 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,58 @@ -The MIT License (MIT) +Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved. -Copyright (c) 2016-2017 Gerasimos Maropoulos +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Gerasimos Maropoulos nor the name of his +username, kataras, may be used to endorse or promote products derived from +this software without specific prior written permission. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + +The Go Programming Language is redistributed with the following disclaimer: + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index aca86cf9..b0bb9021 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,173 @@ -# Iris +# ![Logo created by @santoshanand](logo_white_35_24.png) Iris +A fast, cross-platform and efficient web framework with robust set of well-designed features, written entirely in Go. + +[![Build status](https://api.travis-ci.org/kataras/iris.svg?branch=master&style=flat-square)](https://travis-ci.org/kataras/iris) +[![Report card](https://img.shields.io/badge/report%20card%20-a%2B-F44336.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris) +[![Support forum](https://img.shields.io/badge/support-page-ec2eb4.svg?style=flat-square)](http://support.iris-go.com) +[![Examples](https://img.shields.io/badge/howto-examples-3362c2.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples#table-of-contents) +[![Godocs](https://img.shields.io/badge/docs-%20reference-5272B4.svg?style=flat-square)](https://godoc.org/github.com/kataras/iris) +[![Chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris) +[![Buy me a cup of coffee](https://img.shields.io/badge/support-%20open--source-F4A460.svg?logo=data:image%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIGZpbGw9InJnYigyMjAsMjIwLDIyMCkiIGQ9Ik04ODYuNiwzMDUuM2MtNDUuNywyMDMuMS0xODcsMzEwLjMtNDA5LjYsMzEwLjNoLTc0LjFsLTUxLjUsMzI2LjloLTYybC0zLjIsMjEuMWMtMi4xLDE0LDguNiwyNi40LDIyLjYsMjYuNGgxNTguNWMxOC44LDAsMzQuNy0xMy42LDM3LjctMzIuMmwxLjUtOGwyOS45LTE4OS4zbDEuOS0xMC4zYzIuOS0xOC42LDE4LjktMzIuMiwzNy43LTMyLjJoMjMuNWMxNTMuNSwwLDI3My43LTYyLjQsMzA4LjktMjQyLjdDOTIxLjYsNDA2LjgsOTE2LjcsMzQ4LjYsODg2LjYsMzA1LjN6Ii8%2BPHBhdGggZmlsbD0icmdiKDIyMCwyMjAsMjIwKSIgZD0iTTc5MS45LDgzLjlDNzQ2LjUsMzIuMiw2NjQuNCwxMCw1NTkuNSwxMEgyNTVjLTIxLjQsMC0zOS44LDE1LjUtNDMuMSwzNi44TDg1LDg1MWMtMi41LDE1LjksOS44LDMwLjIsMjUuOCwzMC4ySDI5OWw0Ny4zLTI5OS42bC0xLjUsOS40YzMuMi0yMS4zLDIxLjQtMzYuOCw0Mi45LTM2LjhINDc3YzE3NS41LDAsMzEzLTcxLjIsMzUzLjItMjc3LjVjMS4yLTYuMSwyLjMtMTIuMSwzLjEtMTcuOEM4NDUuMSwxODIuOCw4MzMuMiwxMzAuOCw3OTEuOSw4My45TDc5MS45LDgzLjl6Ii8%2BPC9zdmc%2B)](https://github.com/kataras/iris#buy-me-a-cup-of-coffee) +

-Iris is an efficient and well-designed, cross-platform, web framework with robust set of features.
Build your own high-performance web applications and APIs powered by unlimited potentials and portability.
-Build Status -http://goreportcard.com/report/kataras/iris -Iris support forum Examples for new Gophers -Docs -Chat -Buy me a cup of coffee +This benchmark measures results from 'real-world' instead of 'hello-world' application source code. | Last Update At: July 21, 2016. | Shows: Processing Time Horizontal Graph. | Who did:  Third-party source. Transparent achievement.

-What you say about Iris ✌ +Build your own web applications and portable APIs with the highest performance and countless potentials. + +If you're coming from [Node.js](https://nodejs.org) world, this is the [expressjs](https://github.com/expressjs/express)++ equivalent for the [Go Programming Language](https://golang.org). + +Installation ----------- -

- - - - - - +The only requirement is the [Go Programming Language](https://golang.org/dl/), at least version 1.8 -
+```sh +$ go get -u github.com/kataras/iris +``` - - - +> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes. +For further installation support, please navigate [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework). - - - +```go +package main -
+import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/view" +) +// User is just a bindable object structure. +type User struct { + Username string `json:"username"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + City string `json:"city"` + Age int `json:"age"` +} - -What people say - +func main() { + app := iris.New() + + // Define templates using the std html/template engine. + // Parse and load all files inside "./views" folder with ".html" file extension. + // Reload the templates on each request (development mode). + app.AttachView(view.HTML("./views", ".html").Reload(true)) + + // Regster custom handler for specific http errors. + app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.Context) { + // .Values are used to communicate between handlers, middleware. + errMessage := ctx.Values().GetString("error") + if errMessage != "" { + ctx.Writef("Internal server error: %s", errMessage) + return + } + + ctx.Writef("(Unexpected) internal server error") + }) + + app.Use(func(ctx context.Context) { + ctx.Application().Log("Begin request for path: %s", ctx.Path()) + ctx.Next() + }) + + // app.Done(func(ctx context.Context) {}) + + // Method POST: http://localhost:8080/decode + app.Post("/decode", func(ctx context.Context) { + var user User + ctx.ReadJSON(&user) + ctx.Writef("%s %s is %d years old and comes from %s", user.Firstname, user.Lastname, user.Age, user.City) + }) + + // Method GET: http://localhost:8080/encode + app.Get("/encode", func(ctx context.Context) { + doe := User{ + Username: "Johndoe", + Firstname: "John", + Lastname: "Doe", + City: "Neither FBI knows!!!", + Age: 25, + } + + ctx.JSON(doe) + }) + + // Method GET: http://localhost:8080/profile/anytypeofstring + app.Get("/profile/{username:string}", profileByUsername) + + usersRoutes := app.Party("/users", logThisMiddleware) + { + // Method GET: http://localhost:8080/users/42 + usersRoutes.Get("/{id:int min(1)}", getUserByID) + // Method POST: http://localhost:8080/users/create + usersRoutes.Post("/create", createUser) + } + + // Listen for incoming HTTP/1.x & HTTP/2 clients on localhost port 8080. + app.Run(iris.Addr(":8080"), iris.WithCharset("UTF-8")) +} - -What people say - -
+func logThisMiddleware(ctx context.Context) { + ctx.Application().Log("Path: %s | IP: %s", ctx.Path(), ctx.RemoteAddr()) + + // .Next is required to move forward to the chain of handlers, + // if missing then it stops the execution at this handler. + ctx.Next() +} -If you're coming from Node.js world, this is the expressjs equivalent for the Go Programming Language.
+func profileByUsername(ctx context.Context) { + // .Params are used to get dynamic path parameters. + username := ctx.Params().Get("username") + ctx.ViewData("Username", username) + // renders "./views/users/profile.html" + // with {{ .Username }} equals to the username dynamic path parameter. + ctx.View("users/profile.html") +} -

+func getUserByID(ctx context.Context) { + userID := ctx.Params().Get("id") // Or convert directly using: .Values().GetInt/GetInt64 etc... + // your own db fetch here instead of user :=... + user := User{Username: "username" + userID} + + ctx.XML(user) +} -Legends [♡](https://github.com/kataras/iris#support) ------------ +func createUser(ctx context.Context) { + var user User + err := ctx.ReadForm(&user) + if err != nil { + ctx.Values().Set("error", "creating user, read and parse form failed. "+err.Error()) + ctx.StatusCode(iris.StatusInternalServerError) + return + } + // renders "./views/users/create_verification.html" + // with {{ . }} equals to the User object, i.e {{ .Username }} , {{ .Firstname}} etc... + ctx.ViewData("", user) + ctx.View("users/create_verification.html") +} +``` + +### Reload on source code changes + +```sh +$ go get -u github.com/kataras/rizla +$ cd $GOPATH/src/mywebapp +$ rizla main.go +``` + +> Psst: Wanna go to [_examples](https://github.com/kataras/iris/tree/master/_examples) to see more code-snippets? + +
+Legends + +I'm sorry for taking this personally but I really need to thanks each one of them because they stood up [♡](https://github.com/kataras/iris#support) for me when others trying to "bullying" my personality in order to deflame Iris. + +All of us should read and repsect the official [golang](https://golang.org/conduct) and [iris](CODE-OF-CONDUCT.md) community **Code of Conduct**. This type of commitment and communication is the way of making Go great. + + [Juan Sebastián Suárez Valencia](https://github.com/Juanses) donated 20 EUR at September 11 of 2016 @@ -73,18 +191,47 @@ Legends [♡](https://github.com/kataras/iris#support) [Conrad Steenberg](https://github.com/hengestone) donated 25 EUR at March 23 of 2017 - +

+ + + + + + + + + +
+ + +What people say + + + +What people say + + +
+ + + + + + + + + +

+ +
Feature Overview ----------- - Focus on high performance +- Build RESTful APIs with our expressionist path syntax, i.e `{userid:int min(1)}`, `{asset:path}`, `{lowercase: regexp([a-z]+)}` - Automatically install and serve certificates from https://letsencrypt.org - Robust routing and middleware ecosystem -- Build RESTful APIs -- Choose your favorite routes' path syntax between [httprouter](https://github.com/kataras/iris/blob/v6/_examples/beginner/routes-using-httprouter/main.go) and [gorillamux](https://github.com/kataras/iris/blob/v6/_examples/beginner/routes-using-gorillamux/main.go) - Request-Scoped Transactions - Group API's and subdomains with wildcard support - Body binding for JSON, XML, Forms, can be extended to use your own custom binders @@ -94,7 +241,7 @@ Feature Overview - Graceful shutdown - Limit request body - Localization i18N -- Serve static files +- Serve static and embedded files - Cache - Log requests - Customizable format and output for the logger @@ -122,239 +269,30 @@ Feature Overview - And many others... -Table of Contents +Documentation ----------- - + +Small but practical [examples](https://github.com/kataras/iris/tree/master/_examples#table-of-contents) --they cover each feature. -* [Level: Beginner](_examples/beginner) - * [Hello World](_examples/beginner/hello-world/main.go) - * [Routes (using httprouter)](_examples/beginner/routes-using-httprouter/main.go) - * [Routes (using gorillamux)](_examples/beginner/routes-using-gorillamux/main.go) - * [Internal Application File Logger](_examples/beginner/file-logger/main.go) - * [Write JSON](_examples/beginner/write-json/main.go) - * [Read JSON](_examples/beginner/read-json/main.go) - * [Read Form](_examples/beginner/read-form/main.go) - * [Favicon](_examples/beginner/favicon/main.go) - * [File Server](_examples/beginner/file-server/main.go) - * [Send Files](_examples/beginner/send-files/main.go) - * [Stream Writer](_examples/beginner/stream-writer/main.go) - * [Listen UNIX Socket](_examples/beginner/listen-unix/main.go) - * [Listen TLS](_examples/beginner/listen-tls/main.go) - * [Listen Letsencrypt (Automatic Certifications)](_examples/beginner/listen-letsencrypt/main.go) -* [Level: Intermediate](_examples/intermediate) - * [Send An E-mail](_examples/intermediate/e-mail/main.go) - * [Upload/Read Files](_examples/intermediate/upload-files/main.go) - * [Request Logger](_examples/intermediate/request-logger/main.go) - * [Profiling (pprof)](_examples/intermediate/pprof/main.go) - * [Basic Authentication](_examples/intermediate/basicauth/main.go) - * [HTTP Access Control](_examples/intermediate/cors/main.go) - * [Cache Markdown](_examples/intermediate/cache-markdown/main.go) - * [Localization and Internationalization](_examples/intermediate/i18n/main.go) - * [Recovery](_examples/intermediate/recover/main.go) - * [Graceful Shutdown](_examples/intermediate/graceful-shutdown/main.go) - * [Custom TCP Listener](_examples/intermediate/custom-listener/main.go) - * [Custom HTTP Server](_examples/intermediate/custom-httpserver/main.go) - * [View Engine](_examples/intermediate/view) - * [Overview](_examples/intermediate/view/overview/main.go) - * [Template HTML: Part Zero](_examples/intermediate/view/template_html_0/main.go) - * [Template HTML: Part One](_examples/intermediate/view/template_html_1/main.go) - * [Template HTML: Part Two](_examples/intermediate/view/template_html_2/main.go) - * [Template HTML: Part Three](_examples/intermediate/view/template_html_3/main.go) - * [Template HTML: Part Four](_examples/intermediate/view/template_html_4/main.go) - * [Inject Data Between Handlers](_examples/intermediate/view/context-view-data/main.go) - * [Embedding Templates Into Executable](_examples/intermediate/view/embedding-templates-into-app) - * [Custom Renderer](_examples/intermediate/view/custom-renderer/main.go) - * [Password Hashing](_examples/intermediate/password-hashing/main.go) - * [Sessions](_examples/intermediate/sessions) - * [Overview](_examples/intermediate/sessions/overview/main.go) - * [Encoding & Decoding the Session ID: Secure Cookie](_examples/intermediate/sessions/securecookie/main.go) - * [Standalone](_examples/intermediate/sessions/standalone/main.go) - * [With A Back-End Database](_examples/intermediate/sessions/database/main.go) - * [Flash Messages](_examples/intermediate/flash-messages/main.go) - * [Websockets](_examples/intermediate/websockets) - * [Ridiculous Simple](_examples/intermediate/websockets/ridiculous-simple/main.go) - * [Overview](_examples/intermediate/websockets/overview/main.go) - * [Connection List](_examples/intermediate/websockets/connectionlist/main.go) - * [Native Messages](_examples/intermediate/websockets/naive-messages/main.go) - * [Secure](_examples/intermediate/websockets/secure/main.go) - * [Custom Go Client](_examples/intermediate/websockets/custom-go-client/main.go) -* [Level: Advanced](_examples/advanced) - * [Transactions](_examples/advanced/transactions/main.go) - * [HTTP Testing](_examples/advanced/httptest/main_test.go) - * [Watch & Compile Typescript source files](_examples/advanced/typescript/main.go) - * [Cloud Editor](_examples/advanced/cloud-editor/main.go) - * [Online Visitors](_examples/advanced/online-visitors/main.go) - * [URL Shortener using BoltDB](_examples/advanced/url-shortener/main.go) - * [Subdomains](_examples/advanced/subdomains) - * [Single](_examples/advanced/subdomains/single/main.go) - * [Multi](_examples/advanced/subdomains/multi/main.go) - * [Wildcard](_examples/advanced/subdomains/wildcard/main.go) - -Installation ------------ - -The only requirement is the [Go Programming Language](https://golang.org/dl/), at least 1.8 - -```sh -$ go get gopkg.in/kataras/iris.v6 -``` - -Overview ------------ - -```go -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/cors" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/view" -) - -func main() { - // Receives optional iris.Configuration{}, see ./configuration.go - // for more. - app := iris.New() - - // Order doesn't matter, - // You can split it to different .Adapt calls. - // See ./adaptors folder for more. - app.Adapt( - // adapt a logger which prints all errors to the os.Stdout - iris.DevLogger(), - // adapt the adaptors/httprouter or adaptors/gorillamux - httprouter.New(), - // 5 template engines are supported out-of-the-box: - // - // - standard html/template - // - amber - // - django - // - handlebars - // - pug(jade) - // - // Use the html standard engine for all files inside "./views" folder with extension ".html" - view.HTML("./views", ".html"), - // Cors wrapper to the entire application, allow all origins. - cors.New(cors.Options{AllowedOrigins: []string{"*"}})) - - // http://localhost:6300 - // Method: "GET" - // Render ./views/index.html - app.Get("/", func(ctx *iris.Context) { - ctx.Render("index.html", iris.Map{"Title": "Page Title"}, iris.RenderOptions{"gzip": true}) - }) - - // Group routes, optionally: share middleware, template layout and custom http errors. - userAPI := app.Party("/users", userAPIMiddleware). - Layout("layouts/userLayout.html") - { - // Fire userNotFoundHandler when Not Found - // inside http://localhost:6300/users/*anything - userAPI.OnError(404, userNotFoundHandler) - - // http://localhost:6300/users - // Method: "GET" - userAPI.Get("/", getAllHandler) - - // http://localhost:6300/users/42 - // Method: "GET" - userAPI.Get("/:id", getByIDHandler) - - // http://localhost:6300/users - // Method: "POST" - userAPI.Post("/", saveUserHandler) - } - - // Start the server at 127.0.0.1:6300 - app.Listen(":6300") -} - -func userAPIMiddleware(ctx *iris.Context) { - // your code here... - println("Request: " + ctx.Path()) - ctx.Next() // go to the next handler(s) -} - -func userNotFoundHandler(ctx *iris.Context) { - // your code here... - ctx.HTML(iris.StatusNotFound, "

User page not found

") -} - -func getAllHandler(ctx *iris.Context) { - // your code here... -} - -func getByIDHandler(ctx *iris.Context) { - // take the :id from the path, parse to integer - // and set it to the new userID local variable. - userID, _ := ctx.ParamInt("id") - - // userRepo, imaginary database service <- your only job. - user := userRepo.GetByID(userID) - - // send back a response to the client, - // .JSON: content type as application/json; charset="utf-8" - // iris.StatusOK: with 200 http status code. - // - // send user as it is or make use of any json valid golang type, - // like the iris.Map{"username" : user.Username}. - ctx.JSON(iris.StatusOK, user) -} - -func saveUserHandler(ctx *iris.Context) { - // your code here... -} -``` - -### Reload on source code changes - -```sh -$ go get -u github.com/kataras/rizla -$ cd $GOPATH/src/mywebapp -$ rizla main.go -``` - -### Reload templates on each incoming request - -```go -app.Adapt(view.HTML("./views", ".html").Reload(true)) -``` - - -FAQ & Documentation ------------ - - - -1. [Getting Started with Go+Iris](http://gopherbook.iris-go.com) - -2. Official small but practical [examples](https://github.com/kataras/iris/tree/v6/_examples#table-of-contents) - -3. Navigate through [community examples](https://github.com/iris-contrib/examples) too - -4. [Creating A URL Shortener Service Using Go, Iris, and Bolt](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7) - -5. [Godocs](https://godoc.org/gopkg.in/kataras/iris.v6) for deep documentation - -6. [HISTORY.md](https://github.com//kataras/iris/tree/v6/HISTORY.md) is your best friend, version migrations are released there - - -I'll be glad to talk with you about **your awesome feature requests**, -open a new [discussion](http://support.iris-go.com), you will be heard! +Wanna create your own fast URL Shortener Service Using Iris? --click [here](https://medium.com/@kataras/a-url-shortener-service-using-go-iris-and-bolt-4182f0b00ae7) to learn how. +[Godocs](https://godoc.org/github.com/kataras/iris) --for deep understanding. Support ------------ -- :star: [the project](https://github.com/kataras/iris/stargazers), will help you to follow the upcoming features -- [Donate](https://github.com/kataras/iris#buy-me-a-cup-of-coffee), will help me to continue -- [Post](http://support.iris-go.com) a feature request or report a bug, will help all of us to build a better web, together -- :earth_americas: post [an article](https://dzone.com/articles/a-url-shortener-service-using-go-iris-and-bolt-ger) or [tweet](https://twitter.com/gelnior/status/769100480706379776) and share it with your neighbor +- [Post](http://support.iris-go.com) a feature request or report a bug, will help to make the framework even better. +- :star: and watch [the project](https://github.com/kataras/iris/stargazers), will notify you about updates. +- :earth_americas: publish [an article](https://medium.com/) or share a [tweet](https://twitter.com/) about Iris. +- Donations, will help me to continue. +I'll be glad to talk with you about **your awesome feature requests**, +open a new [discussion](http://support.iris-go.com), you will be heard! + +Thanks in advance! Buy me a cup of coffee? ------------ @@ -364,21 +302,40 @@ Iris is free and open source but developing it has taken thousands of hours of m I spend all my time in the construction of Iris, therefore I have no income value. -Feel free to send **any** amount through paypal +Feel free to send **any** amount through paypal: [![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) > Please check your e-mail after your donation. - Thanks for your gratitude and finance help ♡ + + + Third Party Middleware ------------ -Iris has its own middleware form of `func(ctx *iris.Context)` but it's also compatible with all `net/http` middleware forms using [iris.ToHandler](https://github.com/iris-contrib/middleware/blob/master/cors/cors.go#L33), i.e Negroni's middleware form of `func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)`. +Iris has its own middleware form of `func(ctx context.Context)` but it's also compatible with all `net/http` middleware forms. See [here](https://github.com/kataras/iris/blob/master/_examples/beginner/convert-handlers/negroni-like/main.go). -Here is a small list of Iris compatible middleware, I'm sure you can find more: +I'm sure that each of you have, already, found his own favorite list but here's a small list of third-party handlers: | Middleware | Author | Description | | -----------|--------|-------------| @@ -406,32 +363,16 @@ Feel free to put up a [PR](https://github.com/iris-contrib/middleware) your midd Testing ------------ -The `httptest` package is a simple Iris helper for the httpexpect, a new library for End-to-end HTTP and REST API testing for Go. +The `httptest` package is your way for end-to-end HTTP testing, it uses the httpexpect library created by our friend, [gavv](https://github.com/gavv). -You can find tests by navigating to the source code, -i.e: - -- [context_test.go](https://github.com/kataras/iris/blob/v6/context_test.go) -- [handler_test.go](https://github.com/kataras/iris/blob/v6/handler_test.go) -- [policy_gorillamux_test.go](https://github.com/kataras/iris/blob/v6/policy_gorillamux_test.go) -- [policy_httprouter_test.go](https://github.com/kataras/iris/blob/v6/policy_httprouter_test.go) -- [policy_nativerouter_test.go](https://github.com/kataras/iris/blob/v6/policy_nativerouter_test.go) -- [policy_routerwrapper_test.go](https://github.com/kataras/iris/blob/v6/policy_routerwrapper_test.go) -- [policy_sessions_test.go](https://github.com/kataras/iris/blob/v6/policy_sessions_test.go) -- [response_writer_test.go](https://github.com/kataras/iris/blob/v6/response_writer_test.go) -- [route_test.go](https://github.com/kataras/iris/blob/v6/route_test.go) -- [status_test.go](https://github.com/kataras/iris/blob/v6/status_test.go) -- [transaction_test.go](https://github.com/kataras/iris/blob/v6/transaction_test.go) -- [serializer_test.go](https://github.com/kataras/iris/blob/v6/serializer_test.go) - -A simple test is located to [./_examples/advanced/httptest/main_test.go](https://github.com/kataras/iris/blob/v6/_examples/advanced/httptest/main_test.go) +A simple test is located to [./_examples/intermediate/httptest/main_test.go](https://github.com/kataras/iris/blob/master/_examples/intermediate/httptest/main_test.go) Philosophy ------------ -The Iris philosophy is to provide robust tooling for HTTP, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs. Keep note that, today, iris is faster than nginx itself. +The Iris philosophy is to provide robust tooling for HTTP, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs. Keep note that, today, iris is faster than apache+nginx itself. -Iris does not force you to use any specific ORM or template engine. Iris is routerless which means you can adapt any router you like, [httprouter](https://github.com/kataras/iris/blob/v6/_examples/beginner/routes-using-httprouter/main.go) is the fastest, [gorillamux](https://github.com/kataras/iris/blob/v6/_examples/beginner/routes-using-gorillamux/main.go) has more features. With support for the most used template engines (5), you can quickly craft the perfect application. +Iris does not force you to use any specific ORM or template engine. With support for the most popular template engines, you can quickly craft your perfect application. People @@ -441,7 +382,6 @@ The author of Iris is [@kataras](https://github.com/kataras). However the real Success of Iris belongs to you with your bug reports and feature requests that made this Framework so Unique. - Contact ------------ @@ -451,18 +391,39 @@ Besides the fact that we have a [community chat][Chat] for questions or reports - [Facebook](https://facebook.com/kataras.gopher) - [Linkedin](https://www.linkedin.com/in/gerasimos-maropoulos) - -Codename: ["√Νεxτ"](https://github.com/kataras/iris/blob/v6/HISTORY.md) +Version ------------ +Current: v7 + +Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever". + +Community members can request additional features or report a bug fix for a specific iris version. + + +### Should I upgrade my Iris? + +Developers are not forced to use the latest Iris version, they can use any version in production, they can update at any time they want. + +Testers should upgrade immediately, if you're willing to use Iris in production you can wait a little more longer, transaction should be as safe as possible. + +### Where can I find older versions? + +Each Iris version is independent. Only bug fixes, Router's API and experience are kept. + +Previous versions can be found at [releases page](https://github.com/kataras/iris/releases). + License ------------ Unless otherwise noted, the source files are distributed -under the MIT License found in the [LICENSE file](LICENSE). +under the BSD-3-Clause License found in the [LICENSE file](LICENSE). -Note that some optional components that you may use with Iris requires +Note that some third-party packages that you use with Iris may requires different license agreements. - [Chat]: https://kataras.rocket.chat/channel/iris + + + + diff --git a/_examples/README.md b/_examples/README.md index 88310246..6aca38bd 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -1,18 +1,34 @@ # Examples -This folder provides easy to understand code snippets on how to get started with web development with the Go programming language using the [Iris](https://github.com/kataras/iris) web framework. +This folder provides easy to understand code snippets on how to get started with web development with the Go programming language using the [Iris](https://github.com/kataras/iris) web framework. -It doesn't contains "best ways" neither explains all its features. It's just a simple, practical cookbook for young Go developers! +It doesn't contains "best ways" neither explains all its features. It's just a simple, practical cookbook for young Gophers! -## Table of Contents - - +## Table of contents * [Level: Beginner](beginner) - * [Hello World](beginner/hello-world/main.go) - * [Routes (using httprouter)](beginner/routes-using-httprouter/main.go) - * [Routes (using gorillamux)](beginner/routes-using-gorillamux/main.go) + * [Overview](beginner/overview/main.go) + * [Listening](beginner/listening) + * [Common, with address](beginner/listening/listen-addr/main.go) + * [UNIX socket file](beginner/listening/listen-unix/main.go) + * [TLS](beginner/listening/listen-tls) + * [Letsencrypt (Automatic Certifications)](beginner/listening/listen-letsencrypt/main.go) + * [Custom TCP Listener](beginner/listening/custom-listener/main.go) + * [Configuration](beginner/configuration) + * [Basic way](beginner/configuration/basic/main.go) + * [Functional way](beginner/configuration/functional/main.go) + * [Import from YAML file](beginner/configuration/from-yaml-file/main.go) + * [Import from TOML file](beginner/configuration/from-toml-file/main.go) + * [Routing](beginner/routing) + * [Overview](beginner/routing/main.go) + * [Basic](beginner/routing/basic/main.go) + * [Dynamic Path](beginner/routing/dynamic-path/main.go) + * [Reverse routing](beginner/routing/reverse/main.go) + * [Transform any third-party handler to iris-compatible handler](beginner/convert-handlers) + * [From func(http.ResponseWriter, *http.Request, http.HandlerFunc)](beginner/convert-handlers/negroni-like/main.go) + * [From http.Handler or http.HandlerFunc](beginner/convert-handlers/nethttp/main.go) * [Internal Application File Logger](beginner/file-logger/main.go) + * [Custom HTTP Errors](beginner/http-errors/main.go) * [Write JSON](beginner/write-json/main.go) * [Read JSON](beginner/read-json/main.go) * [Read Form](beginner/read-form/main.go) @@ -20,38 +36,46 @@ It doesn't contains "best ways" neither explains all its features. It's just a s * [File Server](beginner/file-server/main.go) * [Send Files](beginner/send-files/main.go) * [Stream Writer](beginner/stream-writer/main.go) - * [Listen UNIX Socket](beginner/listen-unix/main.go) - * [Listen TLS](beginner/listen-tls/main.go) - * [Listen Letsencrypt (Automatic Certifications)](beginner/listen-letsencrypt/main.go) + * [Send An E-mail](beginner/e-mail/main.go) + * [Upload/Read Files](beginner/upload-files/main.go) + * [Recovery](beginner/recover/main.go) + * [Profiling (pprof)](beginner/pprof/main.go) + * [Request Logger](beginner/request-logger/main.go) + * [Basic Authentication](beginner/basicauth/main.go) * [Level: Intermediate](intermediate) - * [Send An E-mail](intermediate/e-mail/main.go) - * [Upload/Read Files](intermediate/upload-files/main.go) - * [Request Logger](intermediate/request-logger/main.go) - * [Profiling (pprof)](intermediate/pprof/main.go) - * [Basic Authentication](intermediate/basicauth/main.go) + * [Transactions](intermediate/transactions/main.go) + * [HTTP Testing](intermediate/httptest/main_test.go) + * [Watch & Compile Typescript source files](intermediate/typescript/main.go) + * [Cloud Editor](intermediate/cloud-editor/main.go) + * [Serve Embedded Files](intermediate/serve-embedded-files/main.go) * [HTTP Access Control](intermediate/cors/main.go) * [Cache Markdown](intermediate/cache-markdown/main.go) * [Localization and Internationalization](intermediate/i18n/main.go) - * [Recovery](intermediate/recover/main.go) - * [Graceful Shutdown](intermediate/graceful-shutdown/main.go) - * [Custom TCP Listener](intermediate/custom-listener/main.go) - * [Custom HTTP Server](intermediate/custom-httpserver/main.go) + * [Graceful Shutdown](intermediate/graceful-shutdown) + * [Basic and simple](intermediate/graceful-shutdown/basic/main.go) + * [Custom Host](intermediate/graceful-shutdown/custom-host/main.go) + * [Custom HTTP Server](intermediate/custom-httpserver) + * [Iris way](intermediate/custom-httpserver/iris-way/main.go) + * [Standar way](intermediate/custom-httpserver/std-way/main.go) + * [More than one server](intermediate/custom-httpserver/multi/main.go) + * [Custom Context](intermediate/custom-context) + * [Method Overriding](intermediate/custom-context/method-overriding/main.go) + * [Route State](intermediate/route-state/main.go) * [View Engine](intermediate/view) * [Overview](intermediate/view/overview/main.go) - * [Template HTML: Part Zero](intermediate/view/template_html_0/main.go) - * [Template HTML: Part One](intermediate/view/template_html_1/main.go) - * [Template HTML: Part Two](intermediate/view/template_html_2/main.go) - * [Template HTML: Part Three](intermediate/view/template_html_3/main.go) - * [Template HTML: Part Four](intermediate/view/template_html_4/main.go) + * [Hi](intermediate/view/template_html_0/main.go) + * [Showcase one simple Layout](intermediate/view/template_html_1/main.go) + * [Layouts `yield` and `render` tmpl funcs](intermediate/view/template_html_2/main.go) + * [Showcase of the `urlpath` tmpl func](intermediate/view/template_html_3/main.go) + * [Showcase of the `url` tmpl func](intermediate/view/template_html_4/main.go) * [Inject Data Between Handlers](intermediate/view/context-view-data/main.go) - * [Embedding Templates Into Executable](intermediate/view/embedding-templates-into-app) - * [Custom Renderer](intermediate/view/custom-renderer/main.go) - * [Password Hashing](intermediate/password-hashing/main.go) + * [Embedding Templates Into App Executable File](intermediate/view/embedding-templates-into-app) * [Sessions](intermediate/sessions) * [Overview](intermediate/sessions/overview/main.go) * [Encoding & Decoding the Session ID: Secure Cookie](intermediate/sessions/securecookie/main.go) * [Standalone](intermediate/sessions/standalone/main.go) * [With A Back-End Database](intermediate/sessions/database/main.go) + * [Password Hashing](intermediate/sessions/password-hashing/main.go) * [Flash Messages](intermediate/flash-messages/main.go) * [Websockets](intermediate/websockets) * [Ridiculous Simple](intermediate/websockets/ridiculous-simple/main.go) @@ -60,20 +84,14 @@ It doesn't contains "best ways" neither explains all its features. It's just a s * [Native Messages](intermediate/websockets/naive-messages/main.go) * [Secure](intermediate/websockets/secure/main.go) * [Custom Go Client](intermediate/websockets/custom-go-client/main.go) + * [Subdomains](intermediate/subdomains) + * [Single](intermediate/subdomains/single/main.go) + * [Multi](intermediate/subdomains/multi/main.go) + * [Wildcard](intermediate/subdomains/wildcard/main.go) * [Level: Advanced](advanced) - * [Transactions](advanced/transactions/main.go) - * [HTTP Testing](advanced/httptest/main_test.go) - * [Watch & Compile Typescript source files](advanced/typescript/main.go) - * [Cloud Editor](advanced/cloud-editor/main.go) * [Online Visitors](advanced/online-visitors/main.go) * [URL Shortener using BoltDB](advanced/url-shortener/main.go) - * [Subdomains](advanced/subdomains) - * [Single](advanced/subdomains/single/main.go) - * [Multi](advanced/subdomains/multi/main.go) - * [Wildcard](advanced/subdomains/wildcard/main.go) +> Do not forget to [star or watch the project](https://github.com/kataras/iris/stargazers) in order to stay updated with the latest tech trends, it takes some seconds for the sake of go! - -> Don't forget to take a quick look or add your own [examples in the community's repository](https://github.com/iris-contrib/examples)! - -> Developers should read the official [documentation](https://godoc.org/gopkg.in/kataras/iris.v6), in depth, for better understanding. +> Developers should read the official [documentation](https://godoc.org/github.com/kataras/iris) in depth, for deep understanding. diff --git a/_examples/advanced/httptest/main.go b/_examples/advanced/httptest/main.go deleted file mode 100644 index 7302b800..00000000 --- a/_examples/advanced/httptest/main.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/sessions" -) - -func newApp() *iris.Framework { - app := iris.New() - app.Adapt(httprouter.New()) - app.Adapt(sessions.New(sessions.Config{Cookie: "mysessionid"})) - - app.Get("/hello", func(ctx *iris.Context) { - sess := ctx.Session() - if !sess.HasFlash() /* or sess.GetFlash("name") == "", same thing here */ { - ctx.HTML(iris.StatusUnauthorized, "

Unauthorized Page!

") - return - } - - ctx.JSON(iris.StatusOK, iris.Map{ - "Message": "Hello", - "From": sess.GetFlash("name"), - }) - }) - - app.Post("/login", func(ctx *iris.Context) { - sess := ctx.Session() - if !sess.HasFlash() { - sess.SetFlash("name", ctx.FormValue("name")) - } - // let's no redirect, just set the flash message, nothing more. - }) - - return app -} - -func main() { - app := newApp() - app.Listen(":8080") -} diff --git a/_examples/advanced/online-visitors/main.go b/_examples/advanced/online-visitors/main.go index 786c5c35..8206a0c7 100644 --- a/_examples/advanced/online-visitors/main.go +++ b/_examples/advanced/online-visitors/main.go @@ -3,30 +3,27 @@ package main import ( "sync/atomic" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/view" - "gopkg.in/kataras/iris.v6/adaptors/websocket" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + + "github.com/kataras/iris/view" + "github.com/kataras/iris/websocket" ) var ( - app *iris.Framework + app *iris.Application ws websocket.Server ) func init() { // init the server instance app = iris.New() - // adapt a logger in dev mode - app.Adapt(iris.DevLogger()) - // adapt router - app.Adapt(httprouter.New()) - // adapt templaes - app.Adapt(view.HTML("./templates", ".html").Reload(true)) - // adapt websocket + // load templaes + app.AttachView(view.HTML("./templates", ".html").Reload(true)) + // attach websocket server ws = websocket.New(websocket.Config{Endpoint: "/my_endpoint"}) ws.OnConnection(HandleWebsocketConnection) - app.Adapt(ws) + ws.Attach(app) } type page struct { @@ -36,21 +33,23 @@ type page struct { func main() { app.StaticWeb("/js", "./static/assets/js") - h := func(ctx *iris.Context) { - ctx.Render("index.html", page{PageID: "index page"}) + h := func(ctx context.Context) { + ctx.ViewData("", page{PageID: "index page"}) + ctx.View("index.html") } - h2 := func(ctx *iris.Context) { - ctx.Render("other.html", page{PageID: "other page"}) + h2 := func(ctx context.Context) { + ctx.ViewData("", page{PageID: "other page"}) + ctx.View("other.html") } // Open some browser tabs/or windows // and navigate to - // http://localhost:8080/ and http://localhost:8080/other + // http://localhost:8080/ and http://localhost:8080/other multiple times. // Each page has its own online-visitors counter. app.Get("/", h) app.Get("/other", h2) - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } type pageView struct { diff --git a/_examples/advanced/subdomains/multi/main.go b/_examples/advanced/subdomains/multi/main.go deleted file mode 100644 index 8a7bcbd8..00000000 --- a/_examples/advanced/subdomains/multi/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) - // subdomains works with all available routers, like other features too. - app.Adapt(httprouter.New()) - - /* - * Setup static files - */ - - app.StaticWeb("/assets", "./public/assets") - app.StaticWeb("/upload_resources", "./public/upload_resources") - - dashboard := app.Party("dashboard.") - { - dashboard.Get("/", func(ctx *iris.Context) { - ctx.Writef("HEY FROM dashboard") - }) - } - system := app.Party("system.") - { - system.Get("/", func(ctx *iris.Context) { - ctx.Writef("HEY FROM system") - }) - } - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("HEY FROM frontend /") - }) - /* test this on firefox, because the domain is not real (because of .local), on firefox this will fail, but you can test it with other domain */ - app.Listen("domain.local:80") // for beginners: look ../hosts file -} diff --git a/_examples/advanced/url-shortener/main.go b/_examples/advanced/url-shortener/main.go index c8fba621..7a0205e7 100644 --- a/_examples/advanced/url-shortener/main.go +++ b/_examples/advanced/url-shortener/main.go @@ -13,21 +13,11 @@ import ( "time" "github.com/boltdb/bolt" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/view" -) + "github.com/kataras/iris" + "github.com/kataras/iris/context" -// a custom Iris event policy, which will run when server interruped (i.e control+C) -// receives a func() error, most of packages are compatible with that on their Close/Shutdown/Cancel funcs. -func releaser(r func() error) iris.EventPolicy { - return iris.EventPolicy{ - Interrupted: func(app *iris.Framework) { - if err := r(); err != nil { - app.Log(iris.ProdMode, "error while releasing resources: "+err.Error()) - } - }} -} + "github.com/kataras/iris/view" +) func main() { app := iris.New() @@ -36,83 +26,77 @@ func main() { db := NewDB("shortener.db") factory := NewFactory(DefaultGenerator, db) - app.Adapt( - // print all kind of errors and logs at os.Stdout - iris.DevLogger(), - // use the httprouter, you can use adpaotrs/gorillamux if you want - httprouter.New(), - // serve the "./templates" directory's "*.html" files with the HTML std view engine. - view.HTML("./templates", ".html").Reload(true), - // `db.Close` is a `func() error` so it can be a `releaser` too. - // Wrap the db.Close with the releaser in order to be released when app exits or control+C - // You probably never saw that before, clever pattern which I am able to use only with Iris :) - releaser(db.Close), - ) - + // serve the "./templates" directory's "*.html" files with the HTML std view engine. + tmpl := view.HTML("./templates", ".html").Reload(true) // template funcs // // look ./templates/index.html#L16 - app.Adapt(iris.TemplateFuncsPolicy{"isPositive": func(n int) bool { + tmpl.AddFunc("isPositive", func(n int) bool { if n > 0 { return true } return false - }}) + }) + + app.AttachView(tmpl) // Serve static files (css) app.StaticWeb("/static", "./resources") - app.Get("/", func(ctx *iris.Context) { - ctx.MustRender("index.html", iris.Map{"url_count": db.Len()}) + app.Get("/", func(ctx context.Context) { + ctx.ViewData("url_count", db.Len()) + ctx.View("index.html") }) // find and execute a short url by its key // used on http://localhost:8080/u/dsaoj41u321dsa - execShortURL := func(ctx *iris.Context, key string) { + execShortURL := func(ctx context.Context, key string) { if key == "" { - ctx.EmitError(iris.StatusBadRequest) + ctx.StatusCode(iris.StatusBadRequest) return } value := db.Get(key) if value == "" { - ctx.SetStatusCode(iris.StatusNotFound) + ctx.StatusCode(iris.StatusNotFound) ctx.Writef("Short URL for key: '%s' not found", key) return } ctx.Redirect(value, iris.StatusTemporaryRedirect) } - app.Get("/u/:shortkey", func(ctx *iris.Context) { - execShortURL(ctx, ctx.Param("shortkey")) + app.Get("/u/:shortkey", func(ctx context.Context) { + execShortURL(ctx, ctx.Params().Get("shortkey")) }) - app.Post("/shorten", func(ctx *iris.Context) { - data := make(map[string]interface{}, 0) + app.Post("/shorten", func(ctx context.Context) { formValue := ctx.FormValue("url") if formValue == "" { - data["form_result"] = "You need to a enter a URL." + ctx.ViewData("form_result", "You need to a enter a URL") } else { key, err := factory.Gen(formValue) if err != nil { - data["form_result"] = "Invalid URL." + ctx.ViewData("form_result", "Invalid URL") } else { if err = db.Set(key, formValue); err != nil { - data["form_result"] = "Internal error while saving the url" - app.Log(iris.DevMode, "while saving url: "+err.Error()) + ctx.ViewData("form_result", "Internal error while saving the URL") + app.Log("while saving URL: " + err.Error()) } else { - ctx.SetStatusCode(iris.StatusOK) - shortenURL := "http://" + app.Config.VHost + "/u/" + key - data["form_result"] = template.HTML("
" + shortenURL + " 
") + ctx.StatusCode(iris.StatusOK) + shortenURL := "http://" + app.ConfigurationReadOnly().GetVHost() + "/u/" + key + ctx.ViewData("form_result", + template.HTML("
"+shortenURL+" 
")) } } } - data["url_count"] = db.Len() - ctx.Render("index.html", data) + ctx.ViewData("url_count", db.Len()) + ctx.View("index.html") }) - app.Listen("localhost:8080") + app.Run(iris.Addr(":8080")) + + db.Close() } // +------------------------------------------------------------+ diff --git a/_examples/intermediate/basicauth/main.go b/_examples/beginner/basicauth/main.go similarity index 50% rename from _examples/intermediate/basicauth/main.go rename to _examples/beginner/basicauth/main.go index 99e46352..b7e37a26 100644 --- a/_examples/intermediate/basicauth/main.go +++ b/_examples/beginner/basicauth/main.go @@ -3,55 +3,56 @@ package main import ( "time" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/middleware/basicauth" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/middleware/basicauth" ) func main() { app := iris.New() - app.Adapt(iris.DevLogger()) // adapt a simple internal logger to print any errors - app.Adapt(httprouter.New()) // adapt a router, you can use gorillamux too authConfig := basicauth.Config{ Users: map[string]string{"myusername": "mypassword", "mySecondusername": "mySecondpassword"}, Realm: "Authorization Required", // defaults to "Authorization Required" - ContextKey: "mycustomkey", // defaults to "user" + ContextKey: "user", // defaults to "user" Expires: time.Duration(30) * time.Minute, } authentication := basicauth.New(authConfig) - app.Get("/", func(ctx *iris.Context) { ctx.Redirect("/admin") }) - // to global app.Use(authentication) (or app.UseGlobal before the .Listen) + + // to global app.Use(authentication) (or app.UseGlobal before the .Run) // to routes /* - app.Get("/mysecret", authentication, func(ctx *iris.Context) { - username := ctx.GetString("mycustomkey") // the Contextkey from the authConfig + app.Get("/mysecret", authentication, func(ctx context.Context) { + username := ctx.Values().GetString("user") // the Contextkey from the authConfig ctx.Writef("Hello authenticated user: %s ", username) }) */ + app.Get("/", func(ctx context.Context) { ctx.Redirect("/admin") }) + // to party needAuth := app.Party("/admin", authentication) { //http://localhost:8080/admin - needAuth.Get("/", func(ctx *iris.Context) { - username := ctx.GetString("mycustomkey") // the Contextkey from the authConfig + needAuth.Get("/", func(ctx context.Context) { + username := ctx.Values().GetString("mycustomkey") // the Contextkey from the authConfig ctx.Writef("Hello authenticated user: %s from: %s ", username, ctx.Path()) }) // http://localhost:8080/admin/profile - needAuth.Get("/profile", func(ctx *iris.Context) { - username := ctx.GetString("mycustomkey") // the Contextkey from the authConfig + needAuth.Get("/profile", func(ctx context.Context) { + username := ctx.Values().GetString("mycustomkey") // the Contextkey from the authConfig ctx.Writef("Hello authenticated user: %s from: %s ", username, ctx.Path()) }) + // http://localhost:8080/admin/settings - needAuth.Get("/settings", func(ctx *iris.Context) { - username := authConfig.User(ctx) // shortcut for ctx.GetString("mycustomkey") + needAuth.Get("/settings", func(ctx context.Context) { + username := authConfig.User(ctx) // shortcut for ctx.Values().GetString("mycustomkey") ctx.Writef("Hello authenticated user: %s from: %s ", username, ctx.Path()) }) } // open http://localhost:8080/admin - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/configuration/basic/main.go b/_examples/beginner/configuration/basic/main.go new file mode 100644 index 00000000..af24c0bd --- /dev/null +++ b/_examples/beginner/configuration/basic/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "github.com/kataras/iris" +) + +func main() { + app := iris.New() + + // [...] + + // Good when you want to modify the whole configuration. + app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.Configuration{ // default configuration: + DisableBanner: false, + DisableTray: false, + DisableInterruptHandler: false, + DisablePathCorrection: false, + EnablePathEscape: false, + FireMethodNotAllowed: false, + DisableBodyConsumptionOnUnmarshal: false, + DisableAutoFireStatusCode: false, + TimeFormat: "Mon, 02 Jan 2006 15:04:05 GMT", + Charset: "UTF-8", + })) + + // or before run: + // app.Configure(iris.WithConfiguration(...)) + // app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/configuration/from-toml-file/configs/iris.tml b/_examples/beginner/configuration/from-toml-file/configs/iris.tml new file mode 100644 index 00000000..3c2d6f34 --- /dev/null +++ b/_examples/beginner/configuration/from-toml-file/configs/iris.tml @@ -0,0 +1,10 @@ +DisableTray: true +DisablePathCorrection = false +EnablePathEscape = false +FireMethodNotAllowed = true +DisableBodyConsumptionOnUnmarshal = false +TimeFormat = "Mon, 01 Jan 2006 15:04:05 GMT" +Charset = "UTF-8" + +[Other] + MyServerName = "Iris" diff --git a/_examples/beginner/configuration/from-toml-file/main.go b/_examples/beginner/configuration/from-toml-file/main.go new file mode 100644 index 00000000..c5b22d2c --- /dev/null +++ b/_examples/beginner/configuration/from-toml-file/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/kataras/iris" +) + +func main() { + app := iris.New() + + // [...] + + // Good when you have two configurations, one for development and a different one for production use. + app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.TOML("./configs/iris.tml"))) + + // or before run: + // app.Configure(iris.WithConfiguration(iris.TOML("./configs/iris.tml"))) + // app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/configuration/from-yaml-file/configs/iris.yml b/_examples/beginner/configuration/from-yaml-file/configs/iris.yml new file mode 100644 index 00000000..e402d414 --- /dev/null +++ b/_examples/beginner/configuration/from-yaml-file/configs/iris.yml @@ -0,0 +1,7 @@ +DisableTray: true +DisablePathCorrection: false +EnablePathEscape: false +FireMethodNotAllowed: true +DisableBodyConsumptionOnUnmarshal: true +TimeFormat: Mon, 01 Jan 2006 15:04:05 GMT +Charset: UTF-8 \ No newline at end of file diff --git a/_examples/beginner/configuration/from-yaml-file/main.go b/_examples/beginner/configuration/from-yaml-file/main.go new file mode 100644 index 00000000..930ca8e6 --- /dev/null +++ b/_examples/beginner/configuration/from-yaml-file/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/kataras/iris" +) + +func main() { + app := iris.New() + + // [...] + + // Good when you have two configurations, one for development and a different one for production use. + app.Run(iris.Addr(":8080"), iris.WithConfiguration(iris.YAML("./configs/iris.yml"))) + + // or before run: + // app.Configure(iris.WithConfiguration(iris.YAML("./configs/iris.yml"))) + // app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/configuration/functional/main.go b/_examples/beginner/configuration/functional/main.go new file mode 100644 index 00000000..e0d2bb3b --- /dev/null +++ b/_examples/beginner/configuration/functional/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/kataras/iris" +) + +func main() { + app := iris.New() + + // [...] + + // Good when you want to change some of the configuration's field. + // I use that method :) + app.Run(iris.Addr(":8080"), iris.WithoutBanner, iris.WithTray, iris.WithCharset("UTF-8")) + + // or before run: + // app.Configure(iris.WithoutBanner, iris.WithTray, iris.WithCharset("UTF-8")) + // app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/convert-handlers/negroni-like/main.go b/_examples/beginner/convert-handlers/negroni-like/main.go new file mode 100644 index 00000000..c4a1f973 --- /dev/null +++ b/_examples/beginner/convert-handlers/negroni-like/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "net/http" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/handlerconv" +) + +func main() { + app := iris.New() + irisMiddleware := handlerconv.FromStdWithNext(negronilikeTestMiddleware) + app.Use(irisMiddleware) + + // Method GET: http://localhost:8080/ + app.Get("/", func(ctx context.Context) { + ctx.HTML("

Home

") + // this will print an error, + // this route's handler will never be executed because the middleware's criteria not passed. + }) + + // Method GET: http://localhost:8080/ok + app.Get("/ok", func(ctx context.Context) { + ctx.Writef("Hello world!") + // this will print "OK. Hello world!". + }) + + // http://localhost:8080 + // http://localhost:8080/ok + app.Run(iris.Addr(":8080")) +} + +func negronilikeTestMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if r.URL.Path == "/ok" && r.Method == "GET" { + w.Write([]byte("OK. ")) + next(w, r) // go to the next route's handler + return + } + // else print an error and do not forward to the route's handler. + w.WriteHeader(iris.StatusBadRequest) + w.Write([]byte("Bad request")) +} diff --git a/_examples/beginner/convert-handlers/nethttp/main.go b/_examples/beginner/convert-handlers/nethttp/main.go new file mode 100644 index 00000000..995d67b5 --- /dev/null +++ b/_examples/beginner/convert-handlers/nethttp/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "net/http" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/handlerconv" +) + +func main() { + app := iris.New() + irisMiddleware := handlerconv.FromStd(nativeTestMiddleware) + app.Use(irisMiddleware) + + // Method GET: http://localhost:8080/ + app.Get("/", func(ctx context.Context) { + ctx.HTML("Home") + }) + + // Method GET: http://localhost:8080/ok + app.Get("/ok", func(ctx context.Context) { + ctx.HTML("Hello world!") + }) + + // http://localhost:8080 + // http://localhost:8080/ok + app.Run(iris.Addr(":8080")) +} + +func nativeTestMiddleware(w http.ResponseWriter, r *http.Request) { + println("Request path: " + r.URL.Path) +} diff --git a/_examples/intermediate/e-mail/main.go b/_examples/beginner/e-mail/main.go similarity index 55% rename from _examples/intermediate/e-mail/main.go rename to _examples/beginner/e-mail/main.go index f17cf0fa..9ee65ec0 100644 --- a/_examples/intermediate/e-mail/main.go +++ b/_examples/beginner/e-mail/main.go @@ -4,20 +4,16 @@ import ( "bytes" "github.com/kataras/go-mailer" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/view" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/view" ) func main() { app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - // set a root for our templates - app.Adapt(view.HTML("./templates", ".html")) + app.AttachView(view.HTML("./templates", ".html")) // change these to your own settings cfg := mailer.Config{ @@ -38,31 +34,40 @@ func main() { //mailService.Send("iris e-mail test subject", "outside of context before server's listen!", to...) //inside handler - app.Get("/send", func(ctx *iris.Context) { + app.Get("/send", func(ctx context.Context) { content := `

Hello From Iris web framework



This is the rich message body ` err := mailService.Send("iris e-mail just t3st subject", content, to...) if err != nil { - ctx.HTML(200, " Problem while sending the e-mail: "+err.Error()) + ctx.HTML(" Problem while sending the e-mail: " + err.Error()) } else { - ctx.HTML(200, "

SUCCESS

") + ctx.HTML("

SUCCESS

") } }) // send a body by template - app.Get("/send/template", func(ctx *iris.Context) { - // we will not use ctx.Render + app.Get("/send/template", func(ctx context.Context) { + // we will not use ctx.View // because we don't want to render to the client // we need the templates' parsed result as raw bytes // so we make use of the bytes.Buffer which is an io.Writer - // which being expected on app.Render parameter first. + // which being expected on app.View parameter first. // - // the rest of the parameters are the same and the behavior is the same as ctx.Render, + // the rest of the parameters are the same and the behavior is the same as ctx.View, // except the 'where to render' buff := &bytes.Buffer{} - app.Render(buff, "body.html", iris.Map{ + // View executes and writes the result of a template file to the writer. + // + // First parameter is the writer to write the parsed template. + // Second parameter is the relative, to templates directory, template filename, including extension. + // Third parameter is the layout, can be empty string. + // Forth parameter is the bindable data to the template, can be nil. + // + // Use context.View to render templates to the client instead. + // Returns an error on failure, otherwise nil. + app.View(buff, "body.html", "", context.Map{ "Message": " his is the rich message body sent by a template!!", "Footer": "The footer of this e-mail!", }) @@ -71,10 +76,12 @@ func main() { err := mailService.Send("iris e-mail just t3st subject", content, to...) if err != nil { - ctx.HTML(iris.StatusOK, " Problem while sending the e-mail: "+err.Error()) + ctx.StatusCode(iris.StatusBadRequest) + ctx.HTML(" Sent failed with error: " + err.Error()) } else { - ctx.HTML(iris.StatusOK, "

SUCCESS

") + ctx.HTML("

SUCCESS

") } }) - app.Listen(":8080") + + app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/e-mail/templates/mail_body.html b/_examples/beginner/e-mail/templates/mail_body.html similarity index 100% rename from _examples/intermediate/e-mail/templates/mail_body.html rename to _examples/beginner/e-mail/templates/mail_body.html diff --git a/_examples/beginner/favicon/main.go b/_examples/beginner/favicon/main.go index 14341a12..0525aa87 100644 --- a/_examples/beginner/favicon/main.go +++ b/_examples/beginner/favicon/main.go @@ -1,23 +1,25 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - app.Adapt(httprouter.New()) - // This will serve the ./static/favicons/iris_favicon_32_32.ico to: localhost:8080/favicon.ico - app.Favicon("./static/favicons/iris_favicon_32_32.ico") - // app.Favicon("./static/favicons/iris_favicon_32_32.ico", "/favicon_32_32.ico") - // This will serve the ./static/favicons/iris_favicon_32_32.ico to: localhost:8080/favicon_32_32.ico + // This will serve the ./static/favicons/iris_favicon_48_48.ico to: localhost:8080/favicon.ico + app.Favicon("./static/favicons/iris_favicon_48_48.ico") - app.Get("/", func(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, `You should see the favicon now at the side of your browser, - if not, please refresh or clear the browser's cache.`) - }) + // app.Favicon("./static/favicons/iris_favicon_48_48.ico", "/favicon_48_48.ico") + // This will serve the ./static/favicons/iris_favicon_48_48.ico to: localhost:8080/favicon_48_48.ico - app.Listen(":8080") + app.Get("/", func(ctx context.Context) { + ctx.HTML(` press here to see the favicon.ico. + At some browsers like chrome, it should be visible at the top-left side of the browser's window, + because some browsers make requests to the /favicon.ico automatically, + so Iris serves your favicon in that path too (you can change it).`) + }) // if favicon doesn't show to you, try to clear your browser's cache. + + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/favicon/static/favicons/iris_favicon_32_32.ico b/_examples/beginner/favicon/static/favicons/iris_favicon_32_32.ico deleted file mode 100644 index 4dd556e1..00000000 Binary files a/_examples/beginner/favicon/static/favicons/iris_favicon_32_32.ico and /dev/null differ diff --git a/_examples/beginner/favicon/static/favicons/iris_favicon_48_48.ico b/_examples/beginner/favicon/static/favicons/iris_favicon_48_48.ico new file mode 100644 index 00000000..c370da51 Binary files /dev/null and b/_examples/beginner/favicon/static/favicons/iris_favicon_48_48.ico differ diff --git a/_examples/beginner/file-logger/main.go b/_examples/beginner/file-logger/main.go index 0150fa43..738e9ded 100644 --- a/_examples/beginner/file-logger/main.go +++ b/_examples/beginner/file-logger/main.go @@ -1,63 +1,49 @@ package main import ( - "log" "os" + "time" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) -var myLogFile *os.File +// get a filename based on the date, file logs works that way the most times +// but these are just a sugar, you can directly attach a new file logger with .AttachLogger(io.Writer) +func todayFilename() string { + today := time.Now().Format("Jan 02 2006") + return today + ".txt" +} -func init() { - // open an output file - f, err := os.Create("logs.txt") +func newLogFile() *os.File { + filename := todayFilename() + // open an output file, this will append to the today's file if server restarted. + f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { panic(err) } - myLogFile = f -} -func myFileLogger() iris.LoggerPolicy { - - // you can use a *File or an io.Writer, - // we want to log with timestamps so we use the log.New. - myLogger := log.New(myLogFile, "", log.LstdFlags) - - // the logger is just a func, - // will be used in runtime - return func(mode iris.LogMode, message string) { - // optionally, check for production or development log message mode - // two modes: iris.ProdMode and iris.DevMode - if mode == iris.ProdMode { - // log only production-mode log messages - myLogger.Println(message) - } - } + return f } func main() { - // close the log file on exit application - // when panic or iris exited by interupt event or manually by Shutdown. - defer func() { - if err := myLogFile.Close(); err != nil { - panic(err) - } - }() + f := newLogFile() + defer f.Close() app := iris.New() - app.Adapt(myFileLogger()) - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - // for the sake of simplicity, in order see the logs at the ./logs.txt: - app.Log(iris.ProdMode, "You have requested: http://localhost/8080"+ctx.Path()) + // attach the file as logger, remember, iris' app logger is just an io.Writer. + app.AttachLogger(f) + app.Get("/", func(ctx context.Context) { + // for the sake of simplicity, in order see the logs at the ./_today_.txt + ctx.Application().Log("Request: %s\r\n", ctx.Path()) ctx.Writef("hello") }) - // open http://localhost:8080 - // and watch the ./logs.txt file - app.Listen(":8080") + // navigate to http://localhost:8080 + // and open the ./logs.txt file + if err := app.Run(iris.Addr(":8080"), iris.WithoutBanner); err != nil { + app.Log("Shutdown with error: %v", err) + + } } diff --git a/_examples/beginner/file-server/assets/css/styles.css b/_examples/beginner/file-server/assets/css/main.css similarity index 100% rename from _examples/beginner/file-server/assets/css/styles.css rename to _examples/beginner/file-server/assets/css/main.css diff --git a/_examples/beginner/file-server/main.go b/_examples/beginner/file-server/main.go index 08d51648..bc7b8f1f 100644 --- a/_examples/beginner/file-server/main.go +++ b/_examples/beginner/file-server/main.go @@ -1,16 +1,16 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" ) func main() { app := iris.New() - app.Adapt(httprouter.New()) + // first parameter is the request path // second is the operating system directory app.StaticWeb("/static", "./assets") - app.Listen(":8080") + // http://localhost:8080/static/css/main.css + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/hello-world/main.go b/_examples/beginner/hello-world/main.go deleted file mode 100644 index 8ab1af6e..00000000 --- a/_examples/beginner/hello-world/main.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func main() { - app := iris.New() - // Adapt the "httprouter", faster, - // but it has limits on named path parameters' validation, - // you can adapt "gorillamux" if you need regexp path validation! - app.Adapt(httprouter.New()) - - app.HandleFunc("GET", "/", func(ctx *iris.Context) { - ctx.Writef("hello world\n") - }) - - app.Listen(":8080") -} diff --git a/_examples/beginner/http-errors/main.go b/_examples/beginner/http-errors/main.go new file mode 100644 index 00000000..8647c34c --- /dev/null +++ b/_examples/beginner/http-errors/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.Context) { + ctx.HTML("Message: " + ctx.Values().GetString("message") + "") + }) + + app.Get("/", func(ctx context.Context) { + ctx.HTML(`Click here to fire the 500 status code`) + }) + + app.Get("/my500", func(ctx context.Context) { + ctx.Values().Set("message", "this is the error message") + ctx.StatusCode(500) + }) + + app.Get("/u/{firstname:alphabetical}", func(ctx context.Context) { + ctx.Writef("Hello %s", ctx.Values().GetString("firstname")) + }) + + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/listen-letsencrypt/main.go b/_examples/beginner/listen-letsencrypt/main.go deleted file mode 100644 index 782af887..00000000 --- a/_examples/beginner/listen-letsencrypt/main.go +++ /dev/null @@ -1,35 +0,0 @@ -// Package main provide one-line integration with letsencrypt.org -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("Hello from SECURE SERVER!") - }) - - app.Get("/test2", func(ctx *iris.Context) { - ctx.Writef("Welcome to secure server from /test2!") - }) - - app.Get("/redirect", func(ctx *iris.Context) { - ctx.Redirect("/test2") - }) - - // This will provide you automatic certification & key from letsencrypt.org's servers - // it also starts a second 'http://' server which will redirect all 'http://$PATH' requests to 'https://$PATH' - - // NOTE: may not work on local addresses like this, - // use it on a real domain, because - // it uses the "golang.org/x/crypto/acme/autocert" package. - app.ListenLETSENCRYPT("localhost:443") -} diff --git a/_examples/beginner/listen-tls/main.go b/_examples/beginner/listen-tls/main.go deleted file mode 100644 index fb460164..00000000 --- a/_examples/beginner/listen-tls/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -const host = "127.0.0.1:443" - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("Hello from the SECURE server") - }) - - app.Get("/mypath", func(ctx *iris.Context) { - ctx.Writef("Hello from the SECURE server on path /mypath") - }) - - // start a secondary server (HTTP) on port 80, this is a non-blocking func - // redirects all http to the main server which is tls/ssl on port :443 - - iris.Proxy(":80", "https://"+host) - // start the MAIN server (HTTPS) on port 443, this is a blocking func - app.ListenTLS(host, "mycert.cert", "mykey.key") - - // now if you navigate to http://127.0.0.1/mypath it will - // send you back to https://127.0.0.1:443/mypath (https://127.0.0.1/mypath) - // - // go to the listen-letsencrypt example to view how you can integrate your server - // to get automatic certification and key from the letsencrypt.org 's servers. -} diff --git a/_examples/beginner/listen-unix/main.go b/_examples/beginner/listen-unix/main.go deleted file mode 100644 index 68632b7c..00000000 --- a/_examples/beginner/listen-unix/main.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -const host = "127.0.0.1:443" - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.ListenUNIX("/tmp/srv.sock", 0666) -} diff --git a/_examples/beginner/listening/custom-listener/main.go b/_examples/beginner/listening/custom-listener/main.go new file mode 100644 index 00000000..a8a77a27 --- /dev/null +++ b/_examples/beginner/listening/custom-listener/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "net" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from the server") + }) + + app.Get("/mypath", func(ctx context.Context) { + ctx.Writef("Hello from %s", ctx.Path()) + }) + + // create any custom tcp listener, unix sock file or tls tcp listener. + l, err := net.Listen("tcp4", ":8080") + if err != nil { + panic(err) + } + + // use of the custom listener + app.Run(iris.Listener(l)) +} diff --git a/_examples/beginner/listening/listen-addr/main.go b/_examples/beginner/listening/listen-addr/main.go new file mode 100644 index 00000000..1d5b8ce0 --- /dev/null +++ b/_examples/beginner/listening/listen-addr/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

Index /

") + }) + + if err := app.Run(iris.Addr(":8080")); err != nil { + panic(err) + } + +} diff --git a/_examples/beginner/listening/listen-letsencrypt/main.go b/_examples/beginner/listening/listen-letsencrypt/main.go new file mode 100644 index 00000000..9e6ecd91 --- /dev/null +++ b/_examples/beginner/listening/listen-letsencrypt/main.go @@ -0,0 +1,31 @@ +// Package main provide one-line integration with letsencrypt.org +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from SECURE SERVER!") + }) + + app.Get("/test2", func(ctx context.Context) { + ctx.Writef("Welcome to secure server from /test2!") + }) + + app.Get("/redirect", func(ctx context.Context) { + ctx.Redirect("/test2") + }) + + // If http to https auto-redirect is one of your needs + // please look the code inside iris_deprecateed.go.ListenLETSENCRYPT to do it manually. + + // NOTE: This may not work on local addresses like this, + // use it on a real domain, because + // it uses the "golang.org/x/crypto/acme/autocert" package. + app.Run(iris.AutoTLS("localhost:443")) +} diff --git a/_examples/beginner/listening/listen-tls/main.go b/_examples/beginner/listening/listen-tls/main.go new file mode 100644 index 00000000..74e5ffa1 --- /dev/null +++ b/_examples/beginner/listening/listen-tls/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from the SECURE server") + }) + + app.Get("/mypath", func(ctx context.Context) { + ctx.Writef("Hello from the SECURE server on path /mypath") + }) + + // start the server (HTTPS) on port 443, this is a blocking func + app.Run(iris.TLS("127.0.0.1:443", "mycert.cert", "mykey.key")) +} diff --git a/_examples/beginner/listen-tls/mycert.cert b/_examples/beginner/listening/listen-tls/mycert.cert similarity index 100% rename from _examples/beginner/listen-tls/mycert.cert rename to _examples/beginner/listening/listen-tls/mycert.cert diff --git a/_examples/beginner/listen-tls/mykey.key b/_examples/beginner/listening/listen-tls/mykey.key similarity index 100% rename from _examples/beginner/listen-tls/mykey.key rename to _examples/beginner/listening/listen-tls/mykey.key diff --git a/_examples/beginner/listening/listen-unix/main.go b/_examples/beginner/listening/listen-unix/main.go new file mode 100644 index 00000000..b699b136 --- /dev/null +++ b/_examples/beginner/listening/listen-unix/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/core/nettools" +) + +func main() { + app := iris.New() + + l, err := nettools.UNIX("/tmpl/srv.sock", 0666) // see its code to see how you can manually create a new file listener, it's easy. + if err != nil { + panic(err) + } + + app.Run(iris.Listener(l)) +} diff --git a/_examples/beginner/overview/main.go b/_examples/beginner/overview/main.go new file mode 100644 index 00000000..aca8f688 --- /dev/null +++ b/_examples/beginner/overview/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/view" +) + +// User is just a bindable object structure. +type User struct { + Username string `json:"username"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + City string `json:"city"` + Age int `json:"age"` +} + +func main() { + app := iris.New() + + // Define templates using the std html/template engine. + // Parse and load all files inside "./views" folder with ".html" file extension. + // Reload the templates on each request (development mode). + app.AttachView(view.HTML("./views", ".html").Reload(true)) + + // Regster custom handler for specific http errors. + app.OnErrorCode(iris.StatusInternalServerError, func(ctx context.Context) { + // .Values are used to communicate between handlers, middleware. + errMessage := ctx.Values().GetString("error") + if errMessage != "" { + ctx.Writef("Internal server error: %s", errMessage) + return + } + + ctx.Writef("(Unexpected) internal server error") + }) + + app.Use(func(ctx context.Context) { + ctx.Application().Log("Begin request for path: %s", ctx.Path()) + ctx.Next() + }) + + // app.Done(func(ctx context.Context) {]}) + + // Method POST: http://localhost:8080/decode + app.Post("/decode", func(ctx context.Context) { + var user User + ctx.ReadJSON(&user) + ctx.Writef("%s %s is %d years old and comes from %s", user.Firstname, user.Lastname, user.Age, user.City) + }) + + // Method GET: http://localhost:8080/encode + app.Get("/encode", func(ctx context.Context) { + doe := User{ + Username: "Johndoe", + Firstname: "John", + Lastname: "Doe", + City: "Neither FBI knows!!!", + Age: 25, + } + + ctx.JSON(doe) + }) + + // Method GET: http://localhost:8080/profile/anytypeofstring + app.Get("/profile/{username:string}", profileByUsername) + + usersRoutes := app.Party("/users", logThisMiddleware) + { + // Method GET: http://localhost:8080/users/42 + usersRoutes.Get("/{id:int min(1)}", getUserByID) + // Method POST: http://localhost:8080/users/create + usersRoutes.Post("/create", createUser) + } + + // Listen for incoming HTTP/1.x & HTTP/2 clients on localhost port 8080. + app.Run(iris.Addr(":8080"), iris.WithCharset("UTF-8")) +} + +func logThisMiddleware(ctx context.Context) { + ctx.Application().Log("Path: %s | IP: %s", ctx.Path(), ctx.RemoteAddr()) + + // .Next is required to move forward to the chain of handlers, + // if missing then it stops the execution at this handler. + ctx.Next() +} + +func profileByUsername(ctx context.Context) { + // .Params are used to get dynamic path parameters. + username := ctx.Params().Get("username") + ctx.ViewData("Username", username) + // renders "./views/users/profile.html" + // with {{ .Username }} equals to the username dynamic path parameter. + ctx.View("users/profile.html") +} + +func getUserByID(ctx context.Context) { + userID := ctx.Params().Get("id") // Or convert directly using: .Values().GetInt/GetInt64 etc... + // your own db fetch here instead of user :=... + user := User{Username: "username" + userID} + + ctx.XML(user) +} + +func createUser(ctx context.Context) { + var user User + err := ctx.ReadForm(&user) + if err != nil { + ctx.Values().Set("error", "creating user, read and parse form failed. "+err.Error()) + ctx.StatusCode(iris.StatusInternalServerError) + return + } + // renders "./views/users/create_verification.html" + // with {{ . }} equals to the User object, i.e {{ .Username }} , {{ .Firstname}} etc... + ctx.ViewData("", user) + ctx.View("users/create_verification.html") +} diff --git a/_examples/beginner/overview/views/users/create_verification.html b/_examples/beginner/overview/views/users/create_verification.html new file mode 100644 index 00000000..a1981380 --- /dev/null +++ b/_examples/beginner/overview/views/users/create_verification.html @@ -0,0 +1,22 @@ + + Create verification + +

Create Verification

+ + + + + + + + + + + + + + + +
UsernameFirstnameLastnameCityAge
{{ .Username }}{{ .Firstname }}{{ .Lastname }}{{ .City }}{{ .Age }}
+ + diff --git a/_examples/beginner/overview/views/users/profile.html b/_examples/beginner/overview/views/users/profile.html new file mode 100644 index 00000000..7b3ceebc --- /dev/null +++ b/_examples/beginner/overview/views/users/profile.html @@ -0,0 +1,7 @@ + + Profile page + +

Profile

+ {{ .Username }} + + diff --git a/_examples/beginner/pprof/main.go b/_examples/beginner/pprof/main.go new file mode 100644 index 00000000..bf688f87 --- /dev/null +++ b/_examples/beginner/pprof/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" + + "github.com/kataras/iris/middleware/pprof" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

Please click here") + }) + + app.Any("/debug/pprof/{action:path}", pprof.New()) + // ___________ + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/read-form/main.go b/_examples/beginner/read-form/main.go index f659e195..7a571728 100644 --- a/_examples/beginner/read-form/main.go +++ b/_examples/beginner/read-form/main.go @@ -2,9 +2,9 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/view" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/view" ) type Visitor struct { @@ -15,28 +15,27 @@ type Visitor struct { func main() { app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - // set the view html template engine - app.Adapt(view.HTML("./templates", ".html")) - app.Get("/", func(ctx *iris.Context) { - if err := ctx.Render("form.html", nil); err != nil { - ctx.Log(iris.DevMode, err.Error()) + // set the view html template engine + app.AttachView(view.HTML("./templates", ".html").Reload(true)) + + app.Get("/", func(ctx context.Context) { + if err := ctx.View("form.html"); err != nil { + ctx.StatusCode(iris.StatusInternalServerError) + ctx.WriteString(err.Error()) } }) - app.Post("/form_action", func(ctx *iris.Context) { + app.Post("/form_action", func(ctx context.Context) { visitor := Visitor{} err := ctx.ReadForm(&visitor) if err != nil { - ctx.Log(iris.DevMode, "Error when reading form: "+err.Error()) + ctx.StatusCode(iris.StatusInternalServerError) + ctx.WriteString(err.Error()) } ctx.Writef("Visitor: %#v", visitor) }) - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/read-form/templates/form.html b/_examples/beginner/read-form/templates/form.html index af68b075..7f85b962 100644 --- a/_examples/beginner/read-form/templates/form.html +++ b/_examples/beginner/read-form/templates/form.html @@ -4,13 +4,16 @@
-


+ Mail:
+ Select one or more:
+ +
diff --git a/_examples/beginner/read-json/main.go b/_examples/beginner/read-json/main.go index 46358a3c..cb7a5e1f 100644 --- a/_examples/beginner/read-json/main.go +++ b/_examples/beginner/read-json/main.go @@ -1,8 +1,8 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) type Company struct { @@ -11,26 +11,34 @@ type Company struct { Other string } -func MyHandler(ctx *iris.Context) { +func MyHandler(ctx context.Context) { c := &Company{} if err := ctx.ReadJSON(c); err != nil { - ctx.Log(iris.DevMode, err.Error()) + ctx.StatusCode(iris.StatusBadRequest) + ctx.WriteString(err.Error()) return } - ctx.Writef("Company: %#v\n", c) + ctx.Writef("Received: %#v\n", c) } func main() { app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - // use postman or whatever to do a POST request - // to the http://localhost:8080 with BODY: JSON PAYLOAD - // and Content-Type to application/json app.Post("/", MyHandler) - app.Listen(":8080") + + // use Postman or whatever to do a POST request + // to the http://localhost:8080 with RAW BODY: + /* + { + "Name": "Iris-Go", + "City": "New York", + "Other": "Something here" + } + */ + // and Content-Type to application/json + // + // The response should be: + // Received: &main.Company{Name:"Iris-Go", City:"New York", Other:"Something here"} + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/recover/main.go b/_examples/beginner/recover/main.go new file mode 100644 index 00000000..95206cd0 --- /dev/null +++ b/_examples/beginner/recover/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" + + "github.com/kataras/iris/middleware/recover" +) + +func main() { + app := iris.New() + // use this recover(y) middleware + app.Use(recover.New()) + + i := 0 + // let's simmilate a panic every next request + app.Get("/", func(ctx context.Context) { + i++ + if i%2 == 0 { + panic("a panic here") + } + ctx.Writef("Hello, refresh one time more to get panic!") + }) + + // http://localhost:8080, refresh it 5-6 times. + app.Run(iris.Addr(":8080")) +} + +// Note: +// app := iris.Default() instead of iris.New() makes use of the recovery middleware automatically. diff --git a/_examples/intermediate/request-logger/main.go b/_examples/beginner/request-logger/main.go similarity index 52% rename from _examples/intermediate/request-logger/main.go rename to _examples/beginner/request-logger/main.go index 0554be19..3a7a8197 100644 --- a/_examples/intermediate/request-logger/main.go +++ b/_examples/beginner/request-logger/main.go @@ -1,17 +1,14 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/middleware/logger" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/middleware/logger" ) func main() { app := iris.New() - app.Adapt(iris.DevLogger()) // it just enables the print of the iris.DevMode logs. Enable it to view the middleware's messages. - app.Adapt(httprouter.New()) - customLogger := logger.New(logger.Config{ // Status displays status code Status: true, @@ -25,29 +22,31 @@ func main() { app.Use(customLogger) - app.Get("/", func(ctx *iris.Context) { + app.Get("/", func(ctx context.Context) { ctx.Writef("hello") }) - app.Get("/1", func(ctx *iris.Context) { + app.Get("/1", func(ctx context.Context) { ctx.Writef("hello") }) - app.Get("/2", func(ctx *iris.Context) { + app.Get("/2", func(ctx context.Context) { ctx.Writef("hello") }) - // log http errors + // log http errors should be done manually errorLogger := logger.New() - app.OnError(iris.StatusNotFound, func(ctx *iris.Context) { - errorLogger.Serve(ctx) + app.OnErrorCode(iris.StatusNotFound, func(ctx context.Context) { + errorLogger(ctx) ctx.Writef("My Custom 404 error page ") }) // http://localhost:8080 // http://localhost:8080/1 // http://localhost:8080/2 - app.Listen(":8080") + // http://lcoalhost:8080/notfoundhere + // see the output on the console. + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/routes-using-gorillamux/main.go b/_examples/beginner/routes-using-gorillamux/main.go deleted file mode 100644 index 3bcda3f3..00000000 --- a/_examples/beginner/routes-using-gorillamux/main.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/gorillamux" // import the gorillamux adaptor -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) // writes both prod and dev logs to the os.Stdout - app.Adapt(gorillamux.New()) // uses the gorillamux for routing and reverse routing - - // set a custom 404 handler - app.OnError(iris.StatusNotFound, func(ctx *iris.Context) { - ctx.HTML(iris.StatusNotFound, "

custom http error page

") - }) - - app.Get("/healthcheck", h) - - gamesMiddleware := func(ctx *iris.Context) { - println(ctx.Method() + ": " + ctx.Path()) - ctx.Next() - } - - games := app.Party("/games", gamesMiddleware) - { // braces are optional of course, it's just a style of code - games.Get("/{gameID:[0-9]+}/clans", h) - games.Get("/{gameID:[0-9]+}/clans/clan/{publicID:[0-9]+}", h) - games.Get("/{gameID:[0-9]+}/clans/search", h) - - games.Put("/{gameID:[0-9]+}/players/{publicID:[0-9]+}", h) - games.Put("/{gameID:[0-9]+}/clans/clan/{publicID:[0-9]+}", h) - - games.Post("/{gameID:[0-9]+}/clans", h) - games.Post("/{gameID:[0-9]+}/players", h) - games.Post("/{gameID:[0-9]+}/clans/{publicID:[0-9]+}/leave", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/application", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/application/:action", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/invitation", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/invitation/:action", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/delete", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/promote", h) - games.Post("/{gameID:[0-9]+}/clans/{clanPublicID:[0-9]+}/memberships/demote", h) - } - - myroute := app.Get("/anything/{anythingparameter:.*}", func(ctx *iris.Context) { - s := ctx.Param("anythingparameter") - ctx.Writef("The path after /anything is: %s", s) - }) // .ChangeName("myroute") - - app.Get("/reverse_myroute", func(ctx *iris.Context) { - // reverse routing snippet using templates: - // https://github.com/kataras/iris/tree/v6/adaptors/view/_examples/template_html_3 (gorillamux) - // https://github.com/kataras/iris/tree/v6/adaptors/view/_examples/template_html_4 (httprouter) - - myrouteRequestPath := app.Path(myroute.Name(), "anythingparameter", "something/here") - ctx.Writef("Should be '/anything/something/here': %s", myrouteRequestPath) - }) - - p := app.Party("mysubdomain.") - // http://mysubdomain.myhost.com/ - p.Get("/", h) - - app.Listen(":8080") -} - -func h(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "

Path

"+ctx.Path()) -} diff --git a/_examples/beginner/routes-using-httprouter/main.go b/_examples/beginner/routes-using-httprouter/main.go deleted file mode 100644 index 4161b0b2..00000000 --- a/_examples/beginner/routes-using-httprouter/main.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func hello(ctx *iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) -} - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(httprouter.New()) - - app.OnError(iris.StatusNotFound, func(ctx *iris.Context) { - ctx.HTML(iris.StatusNotFound, "

Custom not found handler

") - }) - - app.Get("/", hello) - app.Get("/users/:userid", func(ctx *iris.Context) { - ctx.Writef("Hello user with id: %s", ctx.Param("userid")) - }) - - app.Get("/myfiles/*file", func(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "Hello, the dynamic path after /myfiles is:
"+ctx.Param("file")+"") - }) - - app.Get("/users/:userid/messages/:messageid", func(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, `Message from user with id:
`+ctx.Param("userid")+`, - message id: `+ctx.Param("messageid")+``) - }) - - // http://127.0.0.1:8080/users/42 - // http://127.0.0.1:8080/myfiles/mydirectory/myfile.zip - // http://127.0.0.1:8080/users/42/messages/1 - app.Listen(":8080") -} diff --git a/_examples/beginner/routing/basic/main.go b/_examples/beginner/routing/basic/main.go new file mode 100644 index 00000000..c065fc5b --- /dev/null +++ b/_examples/beginner/routing/basic/main.go @@ -0,0 +1,170 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + // registers a custom handler for 404 not found http (error) status code, + // fires when route not found or manually by ctx.StatusCode(iris.StatusNotFound). + app.OnErrorCode(iris.StatusNotFound, notFoundHandler) + + // GET -> HTTP Method + // / -> Path + // func(ctx context.Context) -> The route's handler. + // + // Third receiver should contains the route's handler(s), they are executed by order. + app.Handle("GET", "/", func(ctx context.Context) { + // navigate to the middle of $GOPATH/src/github.com/kataras/iris/context/context.go + // to overview all context's method (there a lot of them, read that and you will learn how iris works too) + ctx.HTML("Hello from " + ctx.Path()) // Hello from / + }) + + app.Get("/home", func(ctx context.Context) { + ctx.Writef(`Same as app.Handle("GET", "/", [...])`) + }) + + app.Get("/donate", donateHandler, donateFinishHandler) + + // Pssst, don't forget dynamic-path example for more "magic"! + app.Get("/api/users/{userid:int min(1)}", func(ctx context.Context) { + userID, err := ctx.Params().GetInt("userid") + + if err != nil { + ctx.Writef("error while trying to parse userid parameter," + + "this will never happen if :int is being used because if it's not integer it will fire Not Found automatically.") + ctx.StatusCode(iris.StatusBadRequest) + return + } + + ctx.JSON(map[string]interface{}{ + // you can pass any custom structured go value of course. + "user_id": userID, + }) + }) + // app.Post("/", func(ctx context.Context){}) -> for POST http method. + // app.Put("/", func(ctx context.Context){})-> for "PUT" http method. + // app.Delete("/", func(ctx context.Context){})-> for "DELETE" http method. + // app.Options("/", func(ctx context.Context){})-> for "OPTIONS" http method. + // app.Trace("/", func(ctx context.Context){})-> for "TRACE" http method. + // app.Head("/", func(ctx context.Context){})-> for "HEAD" http method. + // app.Connect("/", func(ctx context.Context){})-> for "CONNECT" http method. + // app.Patch("/", func(ctx context.Context){})-> for "PATCH" http method. + // app.Any("/", func(ctx context.Context){}) for all http methods. + + // More than one route can contain the same path with a different http mapped method. + // You can catch any route creation errors with: + // route, err := app.Get(...) + // set a name to a route: route.Name = "myroute" + + // You can also group routes by path prefix, sharing middleware(s) and done handlers. + + adminRoutes := app.Party("/admin", adminMiddleware) + + adminRoutes.Done(func(ctx context.Context) { // executes always last if ctx.Next() + ctx.Application().Log("response sent to " + ctx.Path()) + }) + // adminRoutes.Layout("/views/layouts/admin.html") // set a view layout for these routes, see more at intermediate/view examples. + + // GET: http://localhost:8080/admin + adminRoutes.Get("/", func(ctx context.Context) { + // [...] + ctx.StatusCode(iris.StatusOK) // default is 200 == iris.StatusOK + ctx.HTML("

Hello from admin/

") + + ctx.Next() // in order to execute the party's "Done" Handler(s) + }) + + // GET: http://localhost:8080/admin/login + adminRoutes.Get("/login", func(ctx context.Context) { + // [...] + }) + // POST: http://localhost:8080/admin/login + adminRoutes.Post("/login", func(ctx context.Context) { + // [...] + }) + + // subdomains, easier than ever, should add localhost or 127.0.0.1 into your hosts file, + // etc/hosts on unix or C:/windows/system32/drivers/etc/hosts on windows. + v1 := app.Party("v1.") + { // braces are optional, it's just type of style, to group the routes visually. + + // http://v1.localhost:8080 + v1.Get("/", func(ctx context.Context) { + ctx.HTML("Version 1 API. go to /api/users") + }) + + usersAPI := v1.Party("/api/users") + { + // http://v1.localhost:8080/api/users + usersAPI.Get("/", func(ctx context.Context) { + ctx.Writef("All users") + }) + // http://v1.localhost:8080/api/users/42 + usersAPI.Get("/{userid:int}", func(ctx context.Context) { + ctx.Writef("user with id: %s", ctx.Params().Get("userid")) + }) + } + } + + // wildcard subdomains. + wildcardSubdomain := app.Party("*.") + { + wildcardSubdomain.Get("/", func(ctx context.Context) { + ctx.Writef("Subdomain can be anything, now you're here from: %s", ctx.Subdomain()) + }) + } + + // http://localhost:8080 + // http://localhost:8080/home + // http://localhost:8080/donate + // http://localhost:8080/api/users/42 + // http://localhost:8080/admin + // http://localhost:8080/admin/login + // + // http://localhost:8080/api/users/0 + // http://localhost:8080/api/users/blabla + // http://localhost:8080/wontfound + // + // if hosts edited: + // http://v1.localhost:8080 + // http://v1.localhost:8080/api/users + // http://v1.localhost:8080/api/users/42 + // http://anything.localhost:8080 + app.Run(iris.Addr(":8080")) +} + +func adminMiddleware(ctx context.Context) { + // [...] + ctx.Next() // to move to the next handler, or don't that if you have any auth logic. +} + +func donateHandler(ctx context.Context) { + ctx.Writef("Just like an inline handler, but it can be " + + "used by other package, anywhere in your project.") + + // let's pass a value to the next handler + // Values is the way handlers(or middleware) are communicating between each other. + ctx.Values().Set("donate_url", "https://github.com/kataras/iris#buy-me-a-cup-of-coffee") + ctx.Next() // in order to execute the next handler in the chain, look donate route. +} + +func donateFinishHandler(ctx context.Context) { + // values can be any type of object so we could cast the value to a string + // but Iris provides an easy to do that, if donate_url is not defined, then it returns an empty string instead. + donateURL := ctx.Values().GetString("donate_url") + ctx.Application().Log("donate_url value was: " + donateURL) + ctx.Writef("\n\nDonate sent(?).") +} + +func notFoundHandler(ctx context.Context) { + ctx.HTML("Custom route for 404 not found http code, here you can render a view, html, json any valid response.") +} + +// Notes: +// A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed. +// If route failed to be registered, the app will panic without any warnings +// if you didn't catch the second return value(error) on .Handle/.Get.... diff --git a/_examples/beginner/routing/dynamic-path/main.go b/_examples/beginner/routing/dynamic-path/main.go new file mode 100644 index 00000000..29e747c6 --- /dev/null +++ b/_examples/beginner/routing/dynamic-path/main.go @@ -0,0 +1,183 @@ +package main + +import ( + "strconv" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + // At the previous example "routing/basic", + // we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path + // with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros. + + // Iris, like net/http std package registers route's handlers + // by a Handler, the Iris' type of handler is just a func(ctx context.Context) + // where context comes from github.com/kataras/iris/context. + // Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary. + // + // Iris has the easiest and the most powerful routing process you have ever meet. + // If you're used to use the "httprouter" + // then you don't have to change a thing of a route's path. + // + // At the same time, + // Iris has its own interpeter(yes like a programming language) + // for route's path syntax and their dynamic path parameters parsing and evaluation, + // I am calling them "macros" for shortcut. + // In the following examples we will see only the second option, which has exactly the same speed + // compared to "httprouter". + // How? It calculates its needs and if not any special regexp needed then it just + // registers the route with the underline httprouter's path syntax, + // otherwise it pre-compiles the regexp and adds the necessary middleware(s). + // + // Note: the Iris' router follows the "httprouter"'s rules for routes confliction. + // + // Standard macro types for parameters: + // +------------------------+ + // | {param:string} | + // +------------------------+ + // string type + // anything + // + // +------------------------+ + // | {param:int} | + // +------------------------+ + // int type + // only numbers (0-9) + // + // +------------------------+ + // | {param:alphabetical} | + // +------------------------+ + // alphabetical/letter type + // letters only (upper or lowercase) + // + // +------------------------+ + // | {param:file} | + // +------------------------+ + // file type + // letters (upper or lowercase) + // numbers (0-9) + // underscore (_) + // dash (-) + // point (.) + // no spaces ! or other character + // + // +------------------------+ + // | {param:path} | + // +------------------------+ + // path type + // anything, should be the last part, more than one path segment, + // i.e: /path1/path2/path3 , ctx.Params().GetString("param") == "/path1/path2/path3" + // + // if type is missing then parameter's type is defaulted to string, so + // {param} == {param:string}. + // + // If a function not found on that type then the "string"'s types functions are being used. + // i.e: + // {param:int min(3)} + // + // + // Besides the fact that Iris provides the basic types and some default "macro funcs" + // you are able to register your own too!. + // + // Register a named path parameter function: + // app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool { + // [...] + // return true/false -> true means valid. + // }) + // + // at the func(argument ...) you can have any standard type, it will be validated before the server starts + // so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool. + // + // {param:string equal(iris)} , "iris" will be the argument here: + // app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool { + // return func(paramValue string){ return argument == paramValue } + // }) + + // you can use the "string" type which is valid for a single path parameter that can be anything. + app.Get("/username/{name}", func(ctx context.Context) { + ctx.Writef("Hello %s", ctx.Params().Get("name")) + }) // type is missing = {name:string} + + // Let's register our first macro attached to int macro type. + // "min" = the function + // "minValue" = the argument of the function + // func(string) bool = the macro's path parameter evaluator, this executes in serve time when + // a user requests a path which contains the :int macro type with the min(...) macro parameter function. + app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool { + // do anything before serve here [...] + // at this case we don't need to do anything + return func(paramValue string) bool { + n, err := strconv.Atoi(paramValue) + if err != nil { + return false + } + return n >= minValue + } + }) + + // http://localhost:8080/profile/id>=1 + // this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1 + // macro parameter functions are optional of course. + app.Get("/profile/{id:int min(1)}", func(ctx context.Context) { + // second parameter is the error but it will always nil because we use macros, + // the validaton already happened. + id, _ := ctx.Params().GetInt("id") + ctx.Writef("Hello id: %d", id) + }) + + // to change the error code per route's macro evaluator: + app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx context.Context) { + id, _ := ctx.Params().GetInt("id") + friendid, _ := ctx.Params().GetInt("friendid") + ctx.Writef("Hello id: %d looking for friend id: ", id, friendid) + }) // this will throw e 504 error code instead of 404 if all route's macros not passed. + + // http://localhost:8080/game/a-zA-Z/level/0-9 + // remember, alphabetical is lowercase or uppercase letters only. + app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx context.Context) { + ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level")) + }) + + // let's use a trivial custom regexp that validates a single path parameter + // which its value is only lowercase letters. + + // http://localhost:8080/lowercase/kataras + app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx context.Context) { + ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name")) + }) + + // http://localhost:8080/single_file/app.js + app.Get("/single_file/{myfile:file}", func(ctx context.Context) { + ctx.Writef("file type validates if the parameter value has a form of a file name, got: %s", ctx.Params().Get("myfile")) + }) + + // http://localhost:8080/myfiles/any/directory/here/ + // this is the only macro type that accepts any number of path segments. + app.Get("/myfiles/{directory:path}", func(ctx context.Context) { + ctx.Writef("path type accepts any number of path segments, path after /myfiles/ is: %s", ctx.Params().Get("directory")) + }) // for wildcard path (any number of path segments) without validation you can use: + // /myfiles/*directory + + // "{param}"'s performance is exactly the same of ":param"'s. + + // alternatives -> ":param" for single path parameter and "*paramPath" for wildcard path parameter + // acquire them by ctx.Params().Get as always. + + // WARNING: + // A path parameter name should contain only alphabetical letters, symbols, containing '_' and numbers are NOT allowed. + // If route failed to be registered, the app will panic without any warnings + // if you didn't catch the second return value(error) on .Handle/.Get.... + + // Last, do not confuse ctx.Values() with ctx.Params(). + // Path parameter's values goes to ctx.Params() and context's local storage + // that can be used to communicate between handlers and middleware(s) goes to + // ctx.Values(), path parameters and the rest of any custom values are separated for your own good. + + if err := app.Run(iris.Addr(":8080")); err != nil { + panic(err) + } +} diff --git a/_examples/beginner/routing/main.go b/_examples/beginner/routing/main.go new file mode 100644 index 00000000..ab03f248 --- /dev/null +++ b/_examples/beginner/routing/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "io/ioutil" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +/* +Read: +"basic" +"dynamic-path" +and "reverse" examples if you want to release Iris' real power. +*/ + +const maxBodySize = 1 << 20 + +var app *iris.Application + +func init() { + app = iris.New() +} + +func registerErrors() { + // set a custom 404 handler + app.OnErrorCode(iris.StatusNotFound, func(ctx context.Context) { + ctx.HTML("

custom http error page

") + }) +} + +func registerGamesRoutes() { + gamesMiddleware := func(ctx context.Context) { + println(ctx.Method() + ": " + ctx.Path()) + ctx.Next() + } + + // party is just a group of routes with the same prefix + // and middleware, i.e: "/games" and gamesMiddleware. + games := app.Party("/games", gamesMiddleware) + { // braces are optional of course, it's just a style of code + + // "GET" method + games.Get("/{gameID:int}/clans", h) + games.Get("/{gameID:int}/clans/clan/{clanPublicID:int}", h) + games.Get("/{gameID:int}/clans/search", h) + + // "PUT" method + games.Put("/{gameID:int}/players/{clanPublicID:int}", h) + games.Put("/{gameID:int}/clans/clan/{clanPublicID:int}", h) + // remember: "clanPublicID" should not be changed to other routes with the same prefix. + // "POST" method + games.Post("/{gameID:int}/clans", h) + games.Post("/{gameID:int}/players", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/leave", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/application/{action}", h) // {action} == {action:string} + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/invitation/{action}", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/delete", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/promote", h) + games.Post("/{gameID:int}/clans/{clanPublicID:int}/memberships/demote", h) + } +} + +func registerSubdomains() { + mysubdomain := app.Party("mysubdomain.") + // http://mysubdomain.myhost.com + mysubdomain.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from subdomain: %s , from host: %s, method: %s and path: %s", ctx.Subdomain(), ctx.Host(), ctx.Method(), ctx.Path()) + }) +} +func main() { + registerErrors() + registerGamesRoutes() + registerSubdomains() + + // more random examples below: + + app.Handle("GET", "/healthcheck", h) + + // "POST" method + // this handler reads raw body from the client/request + // and sends back the same body + // remember, we have limit to that body in order + // to protect ourselves from "over heating". + app.Post("/", func(ctx context.Context) { + ctx.SetMaxRequestBodySize(maxBodySize) // set max request body that client can send. + // get request body + b, err := ioutil.ReadAll(ctx.Request().Body) + // if is larger then send a bad request status + if err != nil { + ctx.StatusCode(iris.StatusBadRequest) + ctx.Writef(err.Error()) + return + } + // send back the post body + ctx.Write(b) + }) + + // start the server on 0.0.0.0:8080 + app.Run(iris.Addr(":8080")) +} + +func h(ctx context.Context) { + ctx.HTML("

Path: " + ctx.Path() + "

") +} diff --git a/_examples/beginner/routing/reverse/main.go b/_examples/beginner/routing/reverse/main.go new file mode 100644 index 00000000..d3dfe4ee --- /dev/null +++ b/_examples/beginner/routing/reverse/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/router" +) + +func main() { + app := iris.New() + // need for manually reverse routing when needed outside of view engine. + // you normally don't need it because of the {{ urlpath "routename" "path" "values" "here"}} + rv := router.NewRoutePathReverser(app) + + myroute, _ := app.Get("/anything/{anythingparameter:path}", func(ctx context.Context) { + paramValue := ctx.Params().Get("anythingparameter") + ctx.Writef("The path after /anything is: %s", paramValue) + }) + + // useful for links, altough iris' view engine has the {{ urlpath "routename" "path values"}} already. + app.Get("/reverse_myroute", func(ctx context.Context) { + myrouteRequestPath := rv.Path(myroute.Name, "any/path") + ctx.HTML("Should be /anything/any/path: " + myrouteRequestPath) + }) + + // execute a route, similar to redirect but without redirect :) + app.Get("/execute_myroute", func(ctx context.Context) { + ctx.Exec("GET", "/anything/any/path") // like it was called by the client. + }) + + // http://localhost:8080/reverse_myroute + // http://localhost:8080/execute_myroute + // http://localhost:8080/anything/any/path/here + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/beginner/send-files/main.go b/_examples/beginner/send-files/main.go index 454bfcbc..bc7a4a6a 100644 --- a/_examples/beginner/send-files/main.go +++ b/_examples/beginner/send-files/main.go @@ -1,21 +1,17 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - app.Get("/servezip", func(c *iris.Context) { + app.Get("/", func(ctx context.Context) { file := "./files/first.zip" - c.SendFile(file, "c.zip") + ctx.SendFile(file, "c.zip") }) - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/beginner/stream-writer/main.go b/_examples/beginner/stream-writer/main.go index 3b87aa85..5e290fd5 100644 --- a/_examples/beginner/stream-writer/main.go +++ b/_examples/beginner/stream-writer/main.go @@ -5,20 +5,16 @@ import ( "io" "time" // showcase the delay - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) timeWaitForCloseStream := 4 * time.Second - app.Get("/", func(ctx *iris.Context) { + app.Get("/", func(ctx context.Context) { i := 0 // goroutine in order to no block and just wait, // goroutine is OPTIONAL and not a very good option but it depends on the needs @@ -39,7 +35,7 @@ func main() { time.Sleep(timeWaitForCloseStream) }) - app.Get("/alternative", func(ctx *iris.Context) { + app.Get("/alternative", func(ctx context.Context) { // Send the response in chunks and wait for a second between each chunk. ctx.StreamWriter(func(w io.Writer) bool { for i := 1; i <= 4; i++ { @@ -52,5 +48,5 @@ func main() { }) }) - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/upload-files/main.go b/_examples/beginner/upload-files/main.go similarity index 56% rename from _examples/intermediate/upload-files/main.go rename to _examples/beginner/upload-files/main.go index bfb12863..5f686a78 100644 --- a/_examples/intermediate/upload-files/main.go +++ b/_examples/beginner/upload-files/main.go @@ -8,32 +8,33 @@ import ( "strconv" "time" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/view" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/view" ) func main() { app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(httprouter.New()) - app.Adapt(view.HTML("./templates", ".html")) + + app.AttachView(view.HTML("./templates", ".html")) // Serve the form.html to the user - app.Get("/upload", func(ctx *iris.Context) { + app.Get("/upload", func(ctx context.Context) { //create a token (optionally) now := time.Now().Unix() h := md5.New() io.WriteString(h, strconv.FormatInt(now, 10)) token := fmt.Sprintf("%x", h.Sum(nil)) - //render the form with the token for any use you like - ctx.Render("upload_form.html", token) + + // render the form with the token for any use you like + ctx.ViewData("", token) + ctx.View("upload_form.html") }) // Handle the post request from the upload_form.html to the server - app.Post("/upload", iris.LimitRequestBodySize(10<<20), - func(ctx *iris.Context) { + app.Post("/upload", context.LimitRequestBodySize(10<<20), + func(ctx context.Context) { // or use ctx.SetMaxRequestBodySize(10 << 20) //to limit the uploaded file(s) size. @@ -41,8 +42,8 @@ func main() { file, info, err := ctx.FormFile("uploadfile") if err != nil { - ctx.HTML(iris.StatusInternalServerError, - "Error while uploading: "+err.Error()+"") + ctx.StatusCode(iris.StatusInternalServerError) + ctx.HTML("Error while uploading: " + err.Error() + "") return } @@ -55,8 +56,8 @@ func main() { os.O_WRONLY|os.O_CREATE, 0666) if err != nil { - ctx.HTML(iris.StatusInternalServerError, - "Error while uploading: "+err.Error()+"") + ctx.StatusCode(iris.StatusInternalServerError) + ctx.HTML("Error while uploading: " + err.Error() + "") return } defer out.Close() @@ -64,6 +65,6 @@ func main() { io.Copy(out, file) }) - // start the server at 127.0.0.1:8080 - app.Listen(":8080") + // start the server at http://localhost:8080 + app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/upload-files/templates/upload_form.html b/_examples/beginner/upload-files/templates/upload_form.html similarity index 100% rename from _examples/intermediate/upload-files/templates/upload_form.html rename to _examples/beginner/upload-files/templates/upload_form.html diff --git a/_examples/beginner/write-json/main.go b/_examples/beginner/write-json/main.go index b39ee48a..6d6cf6ff 100644 --- a/_examples/beginner/write-json/main.go +++ b/_examples/beginner/write-json/main.go @@ -1,37 +1,39 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) // User bind struct type User struct { Firstname string `json:"firstname"` Lastname string `json:"lastname"` + City string `json:"city"` Age int `json:"age"` } func main() { app := iris.New() - app.Adapt(httprouter.New()) - app.Post("/decode", func(ctx *iris.Context) { + app.Post("/decode", func(ctx context.Context) { var user User ctx.ReadJSON(&user) - ctx.Writef("%s %s is %d years old!", user.Firstname, user.Lastname, user.Age) + ctx.Writef("%s %s is %d years old and comes from %s!", user.Firstname, user.Lastname, user.Age, user.City) }) - app.Get("/encode", func(ctx *iris.Context) { + app.Get("/encode", func(ctx context.Context) { peter := User{ Firstname: "John", Lastname: "Doe", + City: "Neither FBI knows!!!", Age: 25, } - ctx.JSON(iris.StatusOK, peter) + ctx.StatusCode(iris.StatusOK) + ctx.JSON(peter) }) - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/cache-markdown/main.go b/_examples/intermediate/cache-markdown/main.go index 724d57dd..7d92630d 100644 --- a/_examples/intermediate/cache-markdown/main.go +++ b/_examples/intermediate/cache-markdown/main.go @@ -3,15 +3,16 @@ package main import ( "time" - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/kataras/iris" + "github.com/kataras/iris/cache" + "github.com/kataras/iris/context" ) -var testMarkdownContents = `## Hello Markdown +var markdownContents = []byte(`## Hello Markdown This is a sample of Markdown contents - + Features -------- @@ -39,7 +40,7 @@ All features of Sundown are supported, including: * **Fast processing**. It is fast enough to render on-demand in most web applications without having to cache the output. -* **Thread safety**. You can run multiple parsers in different +* **Routine safety**. You can run multiple parsers in different goroutines without ill effect. There is no dependence on global shared state. @@ -51,32 +52,55 @@ All features of Sundown are supported, including: * **Standards compliant**. Output successfully validates using the W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional. - [this is a link](https://github.com/kataras/iris) ` + [this is a link](https://github.com/kataras/iris) `) +// Cache should not be used on handlers that contain dynamic data. +// Cache is a good and a must-feature on static content, i.e "about page" or for a whole blog site. func main() { app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - app.Get("/hi", app.Cache(func(c *iris.Context) { - c.WriteString("Hi this is a big content, do not try cache on small content it will not make any significant difference!") - }, time.Duration(10)*time.Second)) + // first argument is the handler which response's we want to apply the cache. + // if second argument, expiration, is <=time.Second then the cache tries to set the expiration from the "cache-control" maxage header's value(in seconds) + // and if that header is empty or not exist then it sets a default of 5 minutes. + writeMarkdownCached := cache.Cache(writeMarkdown, 10*time.Second) // or CacheHandler to get the handler - bodyHandler := func(ctx *iris.Context) { - ctx.Markdown(iris.StatusOK, testMarkdownContents) - } + app.Get("/", writeMarkdownCached.ServeHTTP) + // saves its content on the first request and serves it instead of re-calculating the content. + // After 10 seconds it will be cleared and resetted. - expiration := time.Duration(5 * time.Second) - - app.Get("/", app.Cache(bodyHandler, expiration)) - - // if expiration is <=time.Second then the cache tries to set the expiration from the "cache-control" maxage header's value(in seconds) - // // if this header doesn't founds then the default is 5 minutes - app.Get("/cache_control", app.Cache(func(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "

Hello!

") - }, -1)) - - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } + +func writeMarkdown(ctx context.Context) { + // tap multiple times the browser's refresh button and you will + // see this println only once every 10 seconds. + println("Handler executed. Content refreshed.") + + ctx.Markdown(markdownContents) +} + +// Notes: +// Cached handler is not changing your pre-defined headers, +// so it will not send any additional headers to the client. +// The cache happening at the server-side's memory. +// +// see "DefaultRuleSet" in "cache/client/ruleset.go" to see how you can add separated +// rules to each of the cached handlers (`.AddRule`) with the help of "/cache/client/rule"'s definitions. +// +// The default rules are: +/* + // #1 A shared cache MUST NOT use a cached response to a request with an + // Authorization header field + rule.HeaderClaim(ruleset.AuthorizationRule), + // #2 "must-revalidate" and/or + // "s-maxage" response directives are not allowed to be served stale + // (Section 4.2.4) by shared caches. In particular, a response with + // either "max-age=0, must-revalidate" or "s-maxage=0" cannot be used to + // satisfy a subsequent request without revalidating it on the origin + // server. + rule.HeaderClaim(ruleset.MustRevalidateRule), + rule.HeaderClaim(ruleset.ZeroMaxAgeRule), + // #3 custom No-Cache header used inside this library + // for BOTH request and response (after get-cache action) + rule.Header(ruleset.NoCacheRule, ruleset.NoCacheRule) +*/ diff --git a/_examples/advanced/cloud-editor/main.go b/_examples/intermediate/cloud-editor/main.go similarity index 61% rename from _examples/advanced/cloud-editor/main.go rename to _examples/intermediate/cloud-editor/main.go index 6d39db6c..6348ece9 100644 --- a/_examples/advanced/cloud-editor/main.go +++ b/_examples/intermediate/cloud-editor/main.go @@ -1,35 +1,34 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/typescript" // optinally - "gopkg.in/kataras/iris.v6/adaptors/typescript/editor" + "github.com/kataras/iris" + + "github.com/kataras/iris/typescript" // optionally + "github.com/kataras/iris/typescript/editor" ) func main() { app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(httprouter.New()) // adapt a router, order doesn't matters + // adapt a router, order doesn't matters // optionally but good to have, I didn't put inside editor or the editor in the typescript compiler adaptors // because you may use tools like gulp and you may use the editor without the typescript compiler adaptor. // but if you need auto-compilation on .ts, we have a solution: ts := typescript.New() ts.Config.Dir = "./www/scripts/" - app.Adapt(ts) // adapt the typescript compiler adaptor + ts.Attach(app) // attach the typescript compiler adaptor editorConfig := editor.Config{ - Hostname: "127.0.0.1", + Hostname: "localhost", Port: 4444, WorkingDir: "./www/scripts/", // "/path/to/the/client/side/directory/", Username: "myusername", Password: "mypassword", } e := editor.New(editorConfig) - app.Adapt(e) // adapt the editor + e.Attach(app) // attach the editor app.StaticWeb("/", "./www") // serve the index.html - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/advanced/cloud-editor/www/index.html b/_examples/intermediate/cloud-editor/www/index.html similarity index 100% rename from _examples/advanced/cloud-editor/www/index.html rename to _examples/intermediate/cloud-editor/www/index.html diff --git a/_examples/advanced/cloud-editor/www/scripts/app.ts b/_examples/intermediate/cloud-editor/www/scripts/app.ts similarity index 100% rename from _examples/advanced/cloud-editor/www/scripts/app.ts rename to _examples/intermediate/cloud-editor/www/scripts/app.ts diff --git a/_examples/advanced/cloud-editor/www/scripts/tsconfig.json b/_examples/intermediate/cloud-editor/www/scripts/tsconfig.json similarity index 100% rename from _examples/advanced/cloud-editor/www/scripts/tsconfig.json rename to _examples/intermediate/cloud-editor/www/scripts/tsconfig.json diff --git a/_examples/intermediate/cors/main.go b/_examples/intermediate/cors/main.go index 1623a887..0ecc659d 100644 --- a/_examples/intermediate/cors/main.go +++ b/_examples/intermediate/cors/main.go @@ -1,43 +1,57 @@ package main +// We don't have to reinvert the wheel, so we will use a good cors middleware +// as a router wrapper for our entire app. +// Follow the steps below: +// +------------------------------------------------------------+ +// | Cors installation | +// +------------------------------------------------------------+ +// go get -u github.com/rs/cors +// +// +------------------------------------------------------------+ +// | Cors wrapper usage | +// +------------------------------------------------------------+ +// import "github.com/rs/cors" +// +// app := iris.New() +// corsOptions := cors.Options{/* your options here */} +// corsWrapper := cors.New(corsOptions).ServeHTTP +// app.Wrap(corsWrapper) +// +// [your code goes here...] +// +// app.Run(iris.Addr(":8080")) + import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/cors" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" + "github.com/rs/cors" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" ) func main() { app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(httprouter.New()) - - crs := cors.New(cors.Options{ + corsOptions := cors.Options{ AllowedOrigins: []string{"*"}, AllowCredentials: true, - }) + } - app.Adapt(crs) // this line should be added - // adaptor supports cors allowed methods, middleware does not. + corsWrapper := cors.New(corsOptions).ServeHTTP - // if you want per-route-only cors - // then you should check https://github.com/iris-contrib/middleware/tree/master/cors + app.WrapRouter(corsWrapper) v1 := app.Party("/api/v1") { - v1.Post("/home", func(c *iris.Context) { - app.Log(iris.DevMode, "lalala") - c.WriteString("Hello from /home") - }) - v1.Get("/g", func(c *iris.Context) { - app.Log(iris.DevMode, "lalala") - c.WriteString("Hello from /home") - }) - v1.Post("/h", func(c *iris.Context) { - app.Log(iris.DevMode, "lalala") - c.WriteString("Hello from /home") - }) + v1.Get("/", h) + v1.Put("/put", h) + v1.Post("/post", h) } - app.Listen(":8080") + app.Run(iris.Addr(":8080")) +} + +func h(ctx context.Context) { + ctx.Application().Log(ctx.Path()) + ctx.Writef("Hello from %s", ctx.Path()) } diff --git a/_examples/intermediate/custom-context/method-overriding/main.go b/_examples/intermediate/custom-context/method-overriding/main.go new file mode 100644 index 00000000..9853da1f --- /dev/null +++ b/_examples/intermediate/custom-context/method-overriding/main.go @@ -0,0 +1,86 @@ +package main + +// In this package I'll show you how to override the existing Context's functions and methods. +// You can easly navigate to the advanced/custom-context to see how you can add new functions +// to your own context (need a custom handler). +// +// This way is far easier to understand and it's faster when you want to override existing methods: +import ( + "reflect" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" + "github.com/kataras/iris/view" +) + +// Create your own custom Context, put any fields you wanna need. +type MyContext struct { + // Optional Part 1: embed (optional but required if you don't want to override all context's methods) + context.Context // it's the internal/Context.go but you don't need to know it. +} + +var _ context.Context = &MyContext{} // optionally: validate on compile-time if MyContext implements context.Context. + +// Optional Part 2: +// The only one important if you will override the Context with an embedded context.Context inside it. +func (ctx *MyContext) Next() { + context.Next(ctx) +} + +// Override any context's method you want... +// [...] + +func (ctx *MyContext) HTML(htmlContents string) (int, error) { + ctx.Application().Log("Executing .HTML function from MyContext") + + ctx.ContentType("text/html") + return ctx.WriteString(htmlContents) +} + +func main() { + app := iris.New() + // Register a view engine on .html files inside the ./view/** directory. + viewEngine := view.HTML("./view", ".html") + app.AttachView(viewEngine) + + // Register the session manager. + sessionManager := sessions.New(sessions.Config{ + Cookie: "myappcookieid", + }) + app.AttachSessionManager(sessionManager) + + // The only one Required: + // here is how you define how your own context will be created and acquired from the iris' generic context pool. + app.ContextPool.Attach(func() context.Context { + return &MyContext{ + // Optional Part 3: + Context: context.NewContext(app), + } + }) + + // register your route, as you normally do + app.Handle("GET", "/", recordWhichContetsJustForProofOfConcept, func(ctx context.Context) { + // use the context's overridden HTML method. + ctx.HTML("

Hello from my custom context's HTML!

") + }) + + // this will be executed by the MyContext.Context + // if MyContext is not directly define the View function by itself. + app.Handle("GET", "/hi/{firstname:alphabetical}", recordWhichContetsJustForProofOfConcept, func(ctx context.Context) { + firstname := ctx.Values().GetString("firstname") + + ctx.ViewData("firstname", firstname) + ctx.Gzip(true) + + ctx.View("hi.html") + }) + + app.Run(iris.Addr(":8080")) +} + +// should always print "($PATH) Handler is executing from 'MyContext'" +func recordWhichContetsJustForProofOfConcept(ctx context.Context) { + ctx.Application().Log("(%s) Handler is executing from: '%s'", ctx.Path(), reflect.TypeOf(ctx).Elem().Name()) + ctx.Next() +} diff --git a/_examples/intermediate/custom-context/method-overriding/view/hi.html b/_examples/intermediate/custom-context/method-overriding/view/hi.html new file mode 100644 index 00000000..7999c305 --- /dev/null +++ b/_examples/intermediate/custom-context/method-overriding/view/hi.html @@ -0,0 +1 @@ +

Hi {{.firstname}}

\ No newline at end of file diff --git a/_examples/intermediate/custom-httpserver/iris-way/main.go b/_examples/intermediate/custom-httpserver/iris-way/main.go new file mode 100644 index 00000000..04a3b7bc --- /dev/null +++ b/_examples/intermediate/custom-httpserver/iris-way/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "net/http" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from the server") + }) + + app.Get("/mypath", func(ctx context.Context) { + ctx.Writef("Hello from %s", ctx.Path()) + }) + + srv := &http.Server{Addr: ":8080" /* Any custom fields here: Handler and ErrorLog are setted to the server automatically */} + // http://localhost:8080/ + // http://localhost:8080/mypath + app.Run(iris.Server(srv)) // same as app.Run(iris.Addr(":8080")) + + // More: + // see "multi" if you need to use more than one server at the same app. + // + // for a custom listener use: iris.Listener(net.Listener) or + // iris.TLS(cert,key) or iris.AutoTLS(), see "custom-listener" example for those. +} diff --git a/_examples/intermediate/custom-httpserver/main.go b/_examples/intermediate/custom-httpserver/main.go deleted file mode 100644 index 932208cd..00000000 --- a/_examples/intermediate/custom-httpserver/main.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "net/http" - - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.Get("/mypath", func(ctx *iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - }) - - // call .Boot before use the 'app' as an http.Handler on a custom http.Server - app.Boot() - - // create our custom fasthttp server and assign the Handler/Router - fsrv := &http.Server{Handler: app, Addr: ":8080"} - fsrv.ListenAndServe() - - // navigate to http://127.0.0.1:8080/mypath -} diff --git a/_examples/intermediate/custom-httpserver/multi/main.go b/_examples/intermediate/custom-httpserver/multi/main.go new file mode 100644 index 00000000..8203248c --- /dev/null +++ b/_examples/intermediate/custom-httpserver/multi/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "net/http" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from the server") + }) + + app.Get("/mypath", func(ctx context.Context) { + ctx.Writef("Hello from %s", ctx.Path()) + }) + + // Note: It's not needed if the first action is "go app.Run". + if err := app.Build(); err != nil { + panic(err) + } + + // start a secondary server listening on localhost:9090. + // use "go" keyword for Listen functions if you need to use more than one server at the same app. + // + // http://localhost:9090/ + // http://localhost:9090/mypath + srv1 := &http.Server{Addr: ":9090", Handler: app} + go srv1.ListenAndServe() + println("Start a server listening on http://localhost:9090") + + // start a "second-secondary" server listening on localhost:5050. + // + // http://localhost:5050/ + // http://localhost:5050/mypath + srv2 := &http.Server{Addr: ":5050", Handler: app} + go srv2.ListenAndServe() + println("Start a server listening on http://localhost:5050") + + // Note: app.Run is totally optional, we have already built the app with app.Build, + // you can just make a new http.Server instead. + // http://localhost:8080/ + // http://localhost:8080/mypath + app.Run(iris.Addr(":8080")) // Block here. +} diff --git a/_examples/intermediate/custom-httpserver/std-way/main.go b/_examples/intermediate/custom-httpserver/std-way/main.go new file mode 100644 index 00000000..9f9a60e1 --- /dev/null +++ b/_examples/intermediate/custom-httpserver/std-way/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "net/http" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + app.Get("/", func(ctx context.Context) { + ctx.Writef("Hello from the server") + }) + + app.Get("/mypath", func(ctx context.Context) { + ctx.Writef("Hello from %s", ctx.Path()) + }) + + // call .Build before use the 'app' as an http.Handler on a custom http.Server + if err := app.Build(); err != nil { + panic(err) + } + + // create our custom server and assign the Handler/Router + srv := &http.Server{Handler: app, Addr: ":8080"} // you have to set Handler:app and Addr, see "iris-way" which does this automatically. + // http://localhost:8080/ + // http://localhost:8080/mypath + println("Start a server listening on http://localhost:8080") + srv.ListenAndServe() // same as app.Run(iris.Addr(":8080")) + + // Notes: + // Banne and Tray are not shown at all. Same for the Interrupt Handler, even if app's configuration allows them. + // + // `.Run` is the only one function that cares about those three. + + // More: + // see "multi" if you need to use more than one server at the same app. + // + // for a custom listener use: iris.Listener(net.Listener) or + // iris.TLS(cert,key) or iris.AutoTLS(), see "custom-listener" example for those. +} diff --git a/_examples/intermediate/custom-listener/main.go b/_examples/intermediate/custom-listener/main.go deleted file mode 100644 index 39f08848..00000000 --- a/_examples/intermediate/custom-listener/main.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "net" - - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - ctx.Writef("Hello from the server") - }) - - app.Get("/mypath", func(ctx *iris.Context) { - ctx.Writef("Hello from %s", ctx.Path()) - }) - - // create our custom listener - ln, err := net.Listen("tcp4", ":8080") - if err != nil { - panic(err) - } - - // use of the custom listener - app.Serve(ln) -} diff --git a/_examples/intermediate/flash-messages/main.go b/_examples/intermediate/flash-messages/main.go index 0c907568..282df5d8 100644 --- a/_examples/intermediate/flash-messages/main.go +++ b/_examples/intermediate/flash-messages/main.go @@ -1,26 +1,24 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/adaptors/sessions" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" ) func main() { app := iris.New() // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - sess := sessions.New(sessions.Config{Cookie: "myappsessionid"}) - app.Adapt(sess) - app.Get("/set", func(ctx *iris.Context) { + sess := sessions.New(sessions.Config{Cookie: "myappsessionid"}) + app.AttachSessionManager(sess) + + app.Get("/set", func(ctx context.Context) { ctx.Session().SetFlash("name", "iris") ctx.Writef("Message setted, is available for the next request") }) - app.Get("/get", func(ctx *iris.Context) { + app.Get("/get", func(ctx context.Context) { name := ctx.Session().GetFlashString("name") if name == "" { ctx.Writef("Empty name!!") @@ -29,7 +27,7 @@ func main() { ctx.Writef("Hello %s", name) }) - app.Get("/test", func(ctx *iris.Context) { + app.Get("/test", func(ctx context.Context) { name := ctx.Session().GetFlashString("name") if name == "" { ctx.Writef("Empty name!!") @@ -41,5 +39,5 @@ func main() { }) - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/graceful-shutdown/basic/main.go b/_examples/intermediate/graceful-shutdown/basic/main.go new file mode 100644 index 00000000..75c97c2a --- /dev/null +++ b/_examples/intermediate/graceful-shutdown/basic/main.go @@ -0,0 +1,48 @@ +package main + +import ( + stdContext "context" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/host" +) + +// Before continue, please read the below notes: +// +// Current version of Iris is auto-graceful on control+C/command+C +// or whenever host's .Shutdown called. +// +// In order to add a custom interrupt handler(ctrl+c/cmd+c) or +// shutdown manually you have to "schedule a host supervisor's task" or +// use the core/host package manually or use a pure http.Server as we already seen at "custom-server" example. +// +// At this example, we will disable the interrupt handler and set our own interrupt handler. +func main() { + app := iris.New() + // output startup banner and error logs on os.Stdout + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

hi, I just exist in order to see if the server is closed

") + }) + + // tasks are always running in their go-routine by-default. + // + // register custom interrupt handler, fires when ctrl+C/cmd+C pressed. + app.Scheduler.Schedule(host.OnInterrupt(func(proc host.TaskProcess) { + println("Shutdown the server gracefully...") + + timeout := 5 * time.Second // give the server 5 seconds to wait for idle connections. + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + proc.Host().Shutdown(ctx) // Shutdown the underline supervisor's server + })) + + // Start the server and disable the default interrupt handler in order to use our scheduled interrupt task. + app.Run(iris.Addr(":8080"), iris.WithoutInterruptHandler) +} + +// Note: +// You can just use an http.Handler with your own signal notify channel and do that as you did with the net/http +// package. I will not show this way, but you can find many examples on the internet. diff --git a/_examples/intermediate/graceful-shutdown/custom-host/main.go b/_examples/intermediate/graceful-shutdown/custom-host/main.go new file mode 100644 index 00000000..e5c11e0d --- /dev/null +++ b/_examples/intermediate/graceful-shutdown/custom-host/main.go @@ -0,0 +1,72 @@ +package main + +import ( + stdContext "context" + "net/http" + "time" + + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/core/host" +) + +// The difference from its parent main.go is that +// with a custom host we're able to call the host's shutdown +// and be notified about its .Shutdown call. +// Almost the same as before. +func main() { + app := iris.New() + // output startup banner and error logs on os.Stdout + + app.Get("/", func(ctx context.Context) { + ctx.HTML("

hi, I just exist in order to see if the server is closed

") + }) + + // build the framework when all routing-relative actions are declared. + if err := app.Build(); err != nil { + panic(err) + } + + // create our custom host supervisor by adapting + // a custom http server + srv := host.New(&http.Server{Addr: ":8080", Handler: app}) + + // tasks are always running in their go-routine by-default. + // + // register custom interrupt handler, fires when ctrl+C/cmd+C pressed, as we did before. + srv.Schedule(host.OnInterrupt(func(proc host.TaskProcess) { + println("Shutdown the server gracefully...") + + timeout := 5 * time.Second // give the server 5 seconds to wait for idle connections. + ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout) + defer cancel() + proc.Host().DeferFlow() // defer the exit, in order to catch the event + srv.Shutdown(ctx) // Shutdown the supervisor, only this way the proc.Host().Done will be notified + // the proc.Host().Shutdown, closes the underline server but no the supervisor. + // This behavior was choosen because is inneed for custom maintanance services + // (i.e restart server every week without loosing connections) which you can easly adapt with Iris. + })) + + // schedule a task to be notify when the server was closed by our interrutp handler, + // optionally ofcourse. + srv.ScheduleFunc(func(proc host.TaskProcess) { + select { + case <-proc.Host().Done(): // when .Host.Shutdown(ctx) called. + println("Server was closed.") + proc.Host().RestoreFlow() // Restore the flow in order to exit(continue after the srv.ListenAndServe) + } + }) + + // Start our custom host + println("Server is running at :8080") + srv.ListenAndServe() + // Go to the console and press ctrl+c(for windows and linux) or cmd+c for osx. + // The output should be: + // Server is running at:8080 + // Shutdown the server gracefully... + // Server was closed. +} + +// Note: +// You can just use an http.Handler with your own signal notify channel and do that as you did with the net/http +// package. I will not show this way, but you can find many examples on the internet. diff --git a/_examples/intermediate/graceful-shutdown/main.go b/_examples/intermediate/graceful-shutdown/main.go deleted file mode 100644 index 71d08a10..00000000 --- a/_examples/intermediate/graceful-shutdown/main.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "context" - "time" - - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" -) - -func main() { - app := iris.New() - // output startup banner and error logs on os.Stdout - app.Adapt(iris.DevLogger()) - // set the router, you can choose gorillamux too - app.Adapt(httprouter.New()) - - app.Get("/hi", func(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "

hi, I just exist in order to see if the server is closed

") - }) - - app.Adapt(iris.EventPolicy{ - // Interrupt Event means when control+C pressed on terminal. - Interrupted: func(*iris.Framework) { - // shut down gracefully, but wait 5 seconds the maximum before closed - ctx, _ := context.WithTimeout(context.Background(), 5*time.Second) - app.Shutdown(ctx) - }, - }) - - app.Listen(":8080") -} diff --git a/_examples/intermediate/httptest/main.go b/_examples/intermediate/httptest/main.go new file mode 100644 index 00000000..46766f83 --- /dev/null +++ b/_examples/intermediate/httptest/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/sessions" +) + +func main() { + app := iris.New() + + app.AttachSessionManager(sessions.New(sessions.Config{Cookie: "mysessionid"})) + + app.Get("/hello", func(ctx context.Context) { + sess := ctx.Session() + if !sess.HasFlash() { + ctx.HTML("

Unauthorized Page!

") + return + } + + ctx.JSON(context.Map{ + "Message": "Hello", + "From": sess.GetFlash("name"), + }) + }) + + app.Post("/login", func(ctx context.Context) { + sess := ctx.Session() + if !sess.HasFlash() { + sess.SetFlash("name", ctx.FormValue("name")) + } + + }) + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/advanced/httptest/main_test.go b/_examples/intermediate/httptest/main_test.go similarity index 84% rename from _examples/advanced/httptest/main_test.go rename to _examples/intermediate/httptest/main_test.go index e93d319f..6a6a8794 100644 --- a/_examples/advanced/httptest/main_test.go +++ b/_examples/intermediate/httptest/main_test.go @@ -3,7 +3,7 @@ package main import ( "testing" - "gopkg.in/kataras/iris.v6/httptest" + "github.com/kataras/iris/httptest" ) // $ cd _example @@ -27,6 +27,3 @@ func TestNewApp(t *testing.T) { // test /hello nauthorized again, it should be return 401 now (flash should be removed) e.GET("/hello").Expect().Status(401).Body().Equal("

Unauthorized Page!

") } - -// for advanced test examples navigate there: -// https://github.com/gavv/httpexpect/blob/master/_examples/iris_test.go diff --git a/_examples/intermediate/i18n/main.go b/_examples/intermediate/i18n/main.go index 98256043..2bed8570 100644 --- a/_examples/intermediate/i18n/main.go +++ b/_examples/intermediate/i18n/main.go @@ -1,15 +1,13 @@ package main import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/middleware/i18n" + "github.com/kataras/iris" + "github.com/kataras/iris/context" + "github.com/kataras/iris/middleware/i18n" ) func main() { app := iris.New() - app.Adapt(iris.DevLogger()) // adapt a simple internal logger to print any errors - app.Adapt(httprouter.New()) // adapt a router, you can use gorillamux too app.Use(i18n.New(i18n.Config{ Default: "en-US", @@ -19,14 +17,14 @@ func main() { "el-GR": "./locales/locale_el-GR.ini", "zh-CN": "./locales/locale_zh-CN.ini"}})) - app.Get("/", func(ctx *iris.Context) { + app.Get("/", func(ctx context.Context) { // it tries to find the language by: - // ctx.Get("language") , that should be setted on other middleware before the i18n middleware* + // ctx.Values().GetString("language") // if that was empty then // it tries to find from the URLParameter setted on the configuration // if not found then - // it tries to find the language by the "lang" cookie + // it tries to find the language by the "language" cookie // if didn't found then it it set to the Default setted on the configuration // hi is the key, 'kataras' is the %s on the .ini file @@ -36,7 +34,7 @@ func main() { // or: hi := i18n.Translate(ctx, "hi", "kataras") - language := ctx.Get(iris.TranslateLanguageContextKey) // language is the language key, example 'en-US' + language := ctx.Values().GetString(ctx.Application().ConfigurationReadOnly().GetTranslateLanguageContextKey()) // return is form of 'en-US' // The first succeed language found saved at the cookie with name ("language"), // you can change that by changing the value of the: iris.TranslateLanguageContextKey @@ -46,6 +44,6 @@ func main() { // go to http://localhost:8080/?lang=el-GR // or http://localhost:8080 // or http://localhost:8080/?lang=zh-CN - app.Listen(":8080") + app.Run(iris.Addr(":8080")) } diff --git a/_examples/intermediate/pprof/main.go b/_examples/intermediate/pprof/main.go deleted file mode 100644 index cc12a099..00000000 --- a/_examples/intermediate/pprof/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/middleware/pprof" -) - -func main() { - app := iris.New() - app.Adapt(iris.DevLogger()) - app.Adapt(httprouter.New()) - - app.Get("/", func(ctx *iris.Context) { - ctx.HTML(iris.StatusOK, "

Please click here") - }) - - app.Get("/debug/pprof/*action", pprof.New()) - // ___________ - // Note: - // if you prefer gorillamux adaptor, then - // the wildcard for gorilla mux (as you already know) is '{action:.*}' instead of '*action'. - app.Listen(":8080") -} diff --git a/_examples/intermediate/recover/main.go b/_examples/intermediate/recover/main.go deleted file mode 100644 index 1a97c26b..00000000 --- a/_examples/intermediate/recover/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "gopkg.in/kataras/iris.v6" - "gopkg.in/kataras/iris.v6/adaptors/httprouter" - "gopkg.in/kataras/iris.v6/middleware/recover" -) - -func main() { - app := iris.New() - // fast way to enable non-fatal messages to be printed to the user - // (yes in iris even recover's errors are not fatal because it's restarting, - // ProdMode messages are only for things that Iris cannot continue at all, - // these are logged by-default but you can change that behavior too by passing a different LoggerPolicy to the .Adapt) - app.Adapt(iris.DevLogger()) - // adapt a router, you can use gorillamux too - app.Adapt(httprouter.New()) - - // use this recover(y) middleware - app.Use(recover.New()) - - i := 0 - // let's simmilate a panic every next request - app.Get("/", func(ctx *iris.Context) { - i++ - if i%2 == 0 { - panic("a panic here") - } - ctx.Writef("Hello, refresh one time more to get panic!") - }) - - // http://localhost:8080 - app.Listen(":8080") -} diff --git a/_examples/intermediate/route-state/main.go b/_examples/intermediate/route-state/main.go new file mode 100644 index 00000000..300b6da6 --- /dev/null +++ b/_examples/intermediate/route-state/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/kataras/iris" + "github.com/kataras/iris/context" +) + +func main() { + app := iris.New() + + none, _ := app.None("/invisible/{username}", func(ctx context.Context) { + ctx.Writef("Hello %s with method: %s", ctx.Values().GetString("username"), ctx.Method()) + + if from := ctx.Values().GetString("from"); from != "" { + ctx.Writef("\nI see that you're coming from %s", from) + } + }) + + app.Get("/change", func(ctx context.Context) { + + if none.IsOnline() { + none.Method = iris.MethodNone + } else { + none.Method = iris.MethodGet + } + + // refresh re-builds the router at serve-time in order to be notified for its new routes. + app.RefreshRouter() + }) + + app.Get("/execute", func(ctx context.Context) { + // same as navigating to "http://localhost:8080/invisible/kataras" when /change has being invoked and route state changed + // from "offline" to "online" + ctx.Values().Set("from", "/execute") // values and session can be shared when calling Exec from a "foreign" context. + ctx.Exec("GET", "/invisible/kataras") + }) + + app.Run(iris.Addr(":8080")) +} diff --git a/_examples/intermediate/serve-embedded-files/assets/css/bootstrap.min.css b/_examples/intermediate/serve-embedded-files/assets/css/bootstrap.min.css new file mode 100644 index 00000000..b578f18d --- /dev/null +++ b/_examples/intermediate/serve-embedded-files/assets/css/bootstrap.min.css @@ -0,0 +1,7225 @@ +/*! + * Bootstrap v3.3.4 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100% +} + +body { + margin: 0 +} + +article, aside, details, figcaption, figure, footer, header, hgroup, + main, menu, nav, section, summary { + display: block +} + +audio, canvas, progress, video { + display: inline-block; + vertical-align: baseline +} + +audio:not ([controls] ){ + display: none; + height: 0 +} + +[hidden], template { + display: none +} + +a { + background-color: transparent +} + +a:active, a:hover { + outline: 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, strong { + font-weight: 700 +} + +dfn { + font-style: italic +} + +h1 { + margin: .67em 0; + font-size: 2em +} + +mark { + color: #000; + background: #ff0 +} + +small { + font-size: 80% +} + +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +img { + border: 0 +} + +svg:not (:root ){ + overflow: hidden +} + +figure { + margin: 1em 40px +} + +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box +} + +pre { + overflow: auto +} + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em +} + +button, input, optgroup, select, textarea { + margin: 0; + font: inherit; + color: inherit +} + +button { + overflow: visible +} + +button, select { + text-transform: none +} + +button, html input[type=button], input[type=reset], input[type=submit] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], html input[disabled] { + cursor: default +} + +button::-moz-focus-inner, input::-moz-focus-inner { + padding: 0; + border: 0 +} + +input { + line-height: normal +} + +input[type=checkbox], input[type=radio] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0 +} + +input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button + { + height: auto +} + +input[type=search] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield +} + +input[type=search]::-webkit-search-cancel-button, input[type=search]::-webkit-search-decoration + { + -webkit-appearance: none +} + +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid silver +} + +legend { + padding: 0; + border: 0 +} + +textarea { + overflow: auto +} + +optgroup { + font-weight: 700 +} + +table { + border-spacing: 0; + border-collapse: collapse +} + +td, th { + padding: 0 +} + /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, :after, :before { + color: #000 !important; + text-shadow: none !important; + background: 0 0 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important + } + a, a:visited { + text-decoration: underline + } + a[href]:after { + content: " (" attr(href) ")" + } + abbr[title]:after { + content: " (" attr(title) ")" + } + a[href^="javascript:"]:after, a[href^="#"]:after { + content: "" + } + blockquote, pre { + border: 1px solid #999; + page-break-inside: avoid + } + thead { + display: table-header-group + } + img, tr { + page-break-inside: avoid + } + img { + max-width: 100% !important + } + h2, h3, p { + orphans: 3; + widows: 3 + } + h2, h3 { + page-break-after: avoid + } + select { + background: #fff !important + } + .navbar { + display: none + } + .btn>.caret, .dropup>.btn>.caret { + border-top-color: #000 !important + } + .label { + border: 1px solid #000 + } + .table { + border-collapse: collapse !important + } + .table td, .table th { + background-color: #fff !important + } + .table-bordered td, .table-bordered th { + border: 1px solid #ddd !important + } +} + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url(../fonts/glyphicons-halflings-regular.eot); + src: url(../fonts/glyphicons-halflings-regular.eot?#iefix) + format('embedded-opentype'), + url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'), + url(../fonts/glyphicons-halflings-regular.woff) format('woff'), + url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'), + url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) + format('svg') +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: 400; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +.glyphicon-asterisk:before { + content: "\2a" +} + +.glyphicon-plus:before { + content: "\2b" +} + +.glyphicon-eur:before, .glyphicon-euro:before { + content: "\20ac" +} + +.glyphicon-minus:before { + content: "\2212" +} + +.glyphicon-cloud:before { + content: "\2601" +} + +.glyphicon-envelope:before { + content: "\2709" +} + +.glyphicon-pencil:before { + content: "\270f" +} + +.glyphicon-glass:before { + content: "\e001" +} + +.glyphicon-music:before { + content: "\e002" +} + +.glyphicon-search:before { + content: "\e003" +} + +.glyphicon-heart:before { + content: "\e005" +} + +.glyphicon-star:before { + content: "\e006" +} + +.glyphicon-star-empty:before { + content: "\e007" +} + +.glyphicon-user:before { + content: "\e008" +} + +.glyphicon-film:before { + content: "\e009" +} + +.glyphicon-th-large:before { + content: "\e010" +} + +.glyphicon-th:before { + content: "\e011" +} + +.glyphicon-th-list:before { + content: "\e012" +} + +.glyphicon-ok:before { + content: "\e013" +} + +.glyphicon-remove:before { + content: "\e014" +} + +.glyphicon-zoom-in:before { + content: "\e015" +} + +.glyphicon-zoom-out:before { + content: "\e016" +} + +.glyphicon-off:before { + content: "\e017" +} + +.glyphicon-signal:before { + content: "\e018" +} + +.glyphicon-cog:before { + content: "\e019" +} + +.glyphicon-trash:before { + content: "\e020" +} + +.glyphicon-home:before { + content: "\e021" +} + +.glyphicon-file:before { + content: "\e022" +} + +.glyphicon-time:before { + content: "\e023" +} + +.glyphicon-road:before { + content: "\e024" +} + +.glyphicon-download-alt:before { + content: "\e025" +} + +.glyphicon-download:before { + content: "\e026" +} + +.glyphicon-upload:before { + content: "\e027" +} + +.glyphicon-inbox:before { + content: "\e028" +} + +.glyphicon-play-circle:before { + content: "\e029" +} + +.glyphicon-repeat:before { + content: "\e030" +} + +.glyphicon-refresh:before { + content: "\e031" +} + +.glyphicon-list-alt:before { + content: "\e032" +} + +.glyphicon-lock:before { + content: "\e033" +} + +.glyphicon-flag:before { + content: "\e034" +} + +.glyphicon-headphones:before { + content: "\e035" +} + +.glyphicon-volume-off:before { + content: "\e036" +} + +.glyphicon-volume-down:before { + content: "\e037" +} + +.glyphicon-volume-up:before { + content: "\e038" +} + +.glyphicon-qrcode:before { + content: "\e039" +} + +.glyphicon-barcode:before { + content: "\e040" +} + +.glyphicon-tag:before { + content: "\e041" +} + +.glyphicon-tags:before { + content: "\e042" +} + +.glyphicon-book:before { + content: "\e043" +} + +.glyphicon-bookmark:before { + content: "\e044" +} + +.glyphicon-print:before { + content: "\e045" +} + +.glyphicon-camera:before { + content: "\e046" +} + +.glyphicon-font:before { + content: "\e047" +} + +.glyphicon-bold:before { + content: "\e048" +} + +.glyphicon-italic:before { + content: "\e049" +} + +.glyphicon-text-height:before { + content: "\e050" +} + +.glyphicon-text-width:before { + content: "\e051" +} + +.glyphicon-align-left:before { + content: "\e052" +} + +.glyphicon-align-center:before { + content: "\e053" +} + +.glyphicon-align-right:before { + content: "\e054" +} + +.glyphicon-align-justify:before { + content: "\e055" +} + +.glyphicon-list:before { + content: "\e056" +} + +.glyphicon-indent-left:before { + content: "\e057" +} + +.glyphicon-indent-right:before { + content: "\e058" +} + +.glyphicon-facetime-video:before { + content: "\e059" +} + +.glyphicon-picture:before { + content: "\e060" +} + +.glyphicon-map-marker:before { + content: "\e062" +} + +.glyphicon-adjust:before { + content: "\e063" +} + +.glyphicon-tint:before { + content: "\e064" +} + +.glyphicon-edit:before { + content: "\e065" +} + +.glyphicon-share:before { + content: "\e066" +} + +.glyphicon-check:before { + content: "\e067" +} + +.glyphicon-move:before { + content: "\e068" +} + +.glyphicon-step-backward:before { + content: "\e069" +} + +.glyphicon-fast-backward:before { + content: "\e070" +} + +.glyphicon-backward:before { + content: "\e071" +} + +.glyphicon-play:before { + content: "\e072" +} + +.glyphicon-pause:before { + content: "\e073" +} + +.glyphicon-stop:before { + content: "\e074" +} + +.glyphicon-forward:before { + content: "\e075" +} + +.glyphicon-fast-forward:before { + content: "\e076" +} + +.glyphicon-step-forward:before { + content: "\e077" +} + +.glyphicon-eject:before { + content: "\e078" +} + +.glyphicon-chevron-left:before { + content: "\e079" +} + +.glyphicon-chevron-right:before { + content: "\e080" +} + +.glyphicon-plus-sign:before { + content: "\e081" +} + +.glyphicon-minus-sign:before { + content: "\e082" +} + +.glyphicon-remove-sign:before { + content: "\e083" +} + +.glyphicon-ok-sign:before { + content: "\e084" +} + +.glyphicon-question-sign:before { + content: "\e085" +} + +.glyphicon-info-sign:before { + content: "\e086" +} + +.glyphicon-screenshot:before { + content: "\e087" +} + +.glyphicon-remove-circle:before { + content: "\e088" +} + +.glyphicon-ok-circle:before { + content: "\e089" +} + +.glyphicon-ban-circle:before { + content: "\e090" +} + +.glyphicon-arrow-left:before { + content: "\e091" +} + +.glyphicon-arrow-right:before { + content: "\e092" +} + +.glyphicon-arrow-up:before { + content: "\e093" +} + +.glyphicon-arrow-down:before { + content: "\e094" +} + +.glyphicon-share-alt:before { + content: "\e095" +} + +.glyphicon-resize-full:before { + content: "\e096" +} + +.glyphicon-resize-small:before { + content: "\e097" +} + +.glyphicon-exclamation-sign:before { + content: "\e101" +} + +.glyphicon-gift:before { + content: "\e102" +} + +.glyphicon-leaf:before { + content: "\e103" +} + +.glyphicon-fire:before { + content: "\e104" +} + +.glyphicon-eye-open:before { + content: "\e105" +} + +.glyphicon-eye-close:before { + content: "\e106" +} + +.glyphicon-warning-sign:before { + content: "\e107" +} + +.glyphicon-plane:before { + content: "\e108" +} + +.glyphicon-calendar:before { + content: "\e109" +} + +.glyphicon-random:before { + content: "\e110" +} + +.glyphicon-comment:before { + content: "\e111" +} + +.glyphicon-magnet:before { + content: "\e112" +} + +.glyphicon-chevron-up:before { + content: "\e113" +} + +.glyphicon-chevron-down:before { + content: "\e114" +} + +.glyphicon-retweet:before { + content: "\e115" +} + +.glyphicon-shopping-cart:before { + content: "\e116" +} + +.glyphicon-folder-close:before { + content: "\e117" +} + +.glyphicon-folder-open:before { + content: "\e118" +} + +.glyphicon-resize-vertical:before { + content: "\e119" +} + +.glyphicon-resize-horizontal:before { + content: "\e120" +} + +.glyphicon-hdd:before { + content: "\e121" +} + +.glyphicon-bullhorn:before { + content: "\e122" +} + +.glyphicon-bell:before { + content: "\e123" +} + +.glyphicon-certificate:before { + content: "\e124" +} + +.glyphicon-thumbs-up:before { + content: "\e125" +} + +.glyphicon-thumbs-down:before { + content: "\e126" +} + +.glyphicon-hand-right:before { + content: "\e127" +} + +.glyphicon-hand-left:before { + content: "\e128" +} + +.glyphicon-hand-up:before { + content: "\e129" +} + +.glyphicon-hand-down:before { + content: "\e130" +} + +.glyphicon-circle-arrow-right:before { + content: "\e131" +} + +.glyphicon-circle-arrow-left:before { + content: "\e132" +} + +.glyphicon-circle-arrow-up:before { + content: "\e133" +} + +.glyphicon-circle-arrow-down:before { + content: "\e134" +} + +.glyphicon-globe:before { + content: "\e135" +} + +.glyphicon-wrench:before { + content: "\e136" +} + +.glyphicon-tasks:before { + content: "\e137" +} + +.glyphicon-filter:before { + content: "\e138" +} + +.glyphicon-briefcase:before { + content: "\e139" +} + +.glyphicon-fullscreen:before { + content: "\e140" +} + +.glyphicon-dashboard:before { + content: "\e141" +} + +.glyphicon-paperclip:before { + content: "\e142" +} + +.glyphicon-heart-empty:before { + content: "\e143" +} + +.glyphicon-link:before { + content: "\e144" +} + +.glyphicon-phone:before { + content: "\e145" +} + +.glyphicon-pushpin:before { + content: "\e146" +} + +.glyphicon-usd:before { + content: "\e148" +} + +.glyphicon-gbp:before { + content: "\e149" +} + +.glyphicon-sort:before { + content: "\e150" +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151" +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152" +} + +.glyphicon-sort-by-order:before { + content: "\e153" +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154" +} + +.glyphicon-sort-by-attributes:before { + content: "\e155" +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156" +} + +.glyphicon-unchecked:before { + content: "\e157" +} + +.glyphicon-expand:before { + content: "\e158" +} + +.glyphicon-collapse-down:before { + content: "\e159" +} + +.glyphicon-collapse-up:before { + content: "\e160" +} + +.glyphicon-log-in:before { + content: "\e161" +} + +.glyphicon-flash:before { + content: "\e162" +} + +.glyphicon-log-out:before { + content: "\e163" +} + +.glyphicon-new-window:before { + content: "\e164" +} + +.glyphicon-record:before { + content: "\e165" +} + +.glyphicon-save:before { + content: "\e166" +} + +.glyphicon-open:before { + content: "\e167" +} + +.glyphicon-saved:before { + content: "\e168" +} + +.glyphicon-import:before { + content: "\e169" +} + +.glyphicon-export:before { + content: "\e170" +} + +.glyphicon-send:before { + content: "\e171" +} + +.glyphicon-floppy-disk:before { + content: "\e172" +} + +.glyphicon-floppy-saved:before { + content: "\e173" +} + +.glyphicon-floppy-remove:before { + content: "\e174" +} + +.glyphicon-floppy-save:before { + content: "\e175" +} + +.glyphicon-floppy-open:before { + content: "\e176" +} + +.glyphicon-credit-card:before { + content: "\e177" +} + +.glyphicon-transfer:before { + content: "\e178" +} + +.glyphicon-cutlery:before { + content: "\e179" +} + +.glyphicon-header:before { + content: "\e180" +} + +.glyphicon-compressed:before { + content: "\e181" +} + +.glyphicon-earphone:before { + content: "\e182" +} + +.glyphicon-phone-alt:before { + content: "\e183" +} + +.glyphicon-tower:before { + content: "\e184" +} + +.glyphicon-stats:before { + content: "\e185" +} + +.glyphicon-sd-video:before { + content: "\e186" +} + +.glyphicon-hd-video:before { + content: "\e187" +} + +.glyphicon-subtitles:before { + content: "\e188" +} + +.glyphicon-sound-stereo:before { + content: "\e189" +} + +.glyphicon-sound-dolby:before { + content: "\e190" +} + +.glyphicon-sound-5-1:before { + content: "\e191" +} + +.glyphicon-sound-6-1:before { + content: "\e192" +} + +.glyphicon-sound-7-1:before { + content: "\e193" +} + +.glyphicon-copyright-mark:before { + content: "\e194" +} + +.glyphicon-registration-mark:before { + content: "\e195" +} + +.glyphicon-cloud-download:before { + content: "\e197" +} + +.glyphicon-cloud-upload:before { + content: "\e198" +} + +.glyphicon-tree-conifer:before { + content: "\e199" +} + +.glyphicon-tree-deciduous:before { + content: "\e200" +} + +.glyphicon-cd:before { + content: "\e201" +} + +.glyphicon-save-file:before { + content: "\e202" +} + +.glyphicon-open-file:before { + content: "\e203" +} + +.glyphicon-level-up:before { + content: "\e204" +} + +.glyphicon-copy:before { + content: "\e205" +} + +.glyphicon-paste:before { + content: "\e206" +} + +.glyphicon-alert:before { + content: "\e209" +} + +.glyphicon-equalizer:before { + content: "\e210" +} + +.glyphicon-king:before { + content: "\e211" +} + +.glyphicon-queen:before { + content: "\e212" +} + +.glyphicon-pawn:before { + content: "\e213" +} + +.glyphicon-bishop:before { + content: "\e214" +} + +.glyphicon-knight:before { + content: "\e215" +} + +.glyphicon-baby-formula:before { + content: "\e216" +} + +.glyphicon-tent:before { + content: "\26fa" +} + +.glyphicon-blackboard:before { + content: "\e218" +} + +.glyphicon-bed:before { + content: "\e219" +} + +.glyphicon-apple:before { + content: "\f8ff" +} + +.glyphicon-erase:before { + content: "\e221" +} + +.glyphicon-hourglass:before { + content: "\231b" +} + +.glyphicon-lamp:before { + content: "\e223" +} + +.glyphicon-duplicate:before { + content: "\e224" +} + +.glyphicon-piggy-bank:before { + content: "\e225" +} + +.glyphicon-scissors:before { + content: "\e226" +} + +.glyphicon-bitcoin:before { + content: "\e227" +} + +.glyphicon-btc:before { + content: "\e227" +} + +.glyphicon-xbt:before { + content: "\e227" +} + +.glyphicon-yen:before { + content: "\00a5" +} + +.glyphicon-jpy:before { + content: "\00a5" +} + +.glyphicon-ruble:before { + content: "\20bd" +} + +.glyphicon-rub:before { + content: "\20bd" +} + +.glyphicon-scale:before { + content: "\e230" +} + +.glyphicon-ice-lolly:before { + content: "\e231" +} + +.glyphicon-ice-lolly-tasted:before { + content: "\e232" +} + +.glyphicon-education:before { + content: "\e233" +} + +.glyphicon-option-horizontal:before { + content: "\e234" +} + +.glyphicon-option-vertical:before { + content: "\e235" +} + +.glyphicon-menu-hamburger:before { + content: "\e236" +} + +.glyphicon-modal-window:before { + content: "\e237" +} + +.glyphicon-oil:before { + content: "\e238" +} + +.glyphicon-grain:before { + content: "\e239" +} + +.glyphicon-sunglasses:before { + content: "\e240" +} + +.glyphicon-text-size:before { + content: "\e241" +} + +.glyphicon-text-color:before { + content: "\e242" +} + +.glyphicon-text-background:before { + content: "\e243" +} + +.glyphicon-object-align-top:before { + content: "\e244" +} + +.glyphicon-object-align-bottom:before { + content: "\e245" +} + +.glyphicon-object-align-horizontal:before { + content: "\e246" +} + +.glyphicon-object-align-left:before { + content: "\e247" +} + +.glyphicon-object-align-vertical:before { + content: "\e248" +} + +.glyphicon-object-align-right:before { + content: "\e249" +} + +.glyphicon-triangle-right:before { + content: "\e250" +} + +.glyphicon-triangle-left:before { + content: "\e251" +} + +.glyphicon-triangle-bottom:before { + content: "\e252" +} + +.glyphicon-triangle-top:before { + content: "\e253" +} + +.glyphicon-console:before { + content: "\e254" +} + +.glyphicon-superscript:before { + content: "\e255" +} + +.glyphicon-subscript:before { + content: "\e256" +} + +.glyphicon-menu-left:before { + content: "\e257" +} + +.glyphicon-menu-right:before { + content: "\e258" +} + +.glyphicon-menu-down:before { + content: "\e259" +} + +.glyphicon-menu-up:before { + content: "\e260" +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +:after, :before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0) +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff +} + +button, input, select, textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +a { + color: #337ab7; + text-decoration: none +} + +a:focus, a:hover { + color: #23527c; + text-decoration: underline +} + +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +figure { + margin: 0 +} + +img { + vertical-align: middle +} + +.carousel-inner>.item>a>img, .carousel-inner>.item>img, .img-responsive, + .thumbnail a>img, .thumbnail>img { + display: block; + max-width: 100%; + height: auto +} + +.img-rounded { + border-radius: 6px +} + +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out +} + +.img-circle { + border-radius: 50% +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0 +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto +} + +[role=button] { + cursor: pointer +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit +} + +.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, .h4 .small, + .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h1 .small, h1 small, + h2 .small, h2 small, h3 .small, h3 small, h4 .small, h4 small, h5 .small, + h5 small, h6 .small, h6 small { + font-weight: 400; + line-height: 1; + color: #777 +} + +.h1, .h2, .h3, h1, h2, h3 { + margin-top: 20px; + margin-bottom: 10px +} + +.h1 .small, .h1 small, .h2 .small, .h2 small, .h3 .small, .h3 small, h1 .small, + h1 small, h2 .small, h2 small, h3 .small, h3 small { + font-size: 65% +} + +.h4, .h5, .h6, h4, h5, h6 { + margin-top: 10px; + margin-bottom: 10px +} + +.h4 .small, .h4 small, .h5 .small, .h5 small, .h6 .small, .h6 small, h4 .small, + h4 small, h5 .small, h5 small, h6 .small, h6 small { + font-size: 75% +} + +.h1, h1 { + font-size: 36px +} + +.h2, h2 { + font-size: 30px +} + +.h3, h3 { + font-size: 24px +} + +.h4, h4 { + font-size: 18px +} + +.h5, h5 { + font-size: 14px +} + +.h6, h6 { + font-size: 12px +} + +p { + margin: 0 0 10px +} + +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4 +} + +@media ( min-width :768px) { + .lead { + font-size: 21px + } +} + +.small, small { + font-size: 85% +} + +.mark, mark { + padding: .2em; + background-color: #fcf8e3 +} + +.text-left { + text-align: left +} + +.text-right { + text-align: right +} + +.text-center { + text-align: center +} + +.text-justify { + text-align: justify +} + +.text-nowrap { + white-space: nowrap +} + +.text-lowercase { + text-transform: lowercase +} + +.text-uppercase { + text-transform: uppercase +} + +.text-capitalize { + text-transform: capitalize +} + +.text-muted { + color: #777 +} + +.text-primary { + color: #337ab7 +} + +a.text-primary:hover { + color: #286090 +} + +.text-success { + color: #3c763d +} + +a.text-success:hover { + color: #2b542c +} + +.text-info { + color: #31708f +} + +a.text-info:hover { + color: #245269 +} + +.text-warning { + color: #8a6d3b +} + +a.text-warning:hover { + color: #66512c +} + +.text-danger { + color: #a94442 +} + +a.text-danger:hover { + color: #843534 +} + +.bg-primary { + color: #fff; + background-color: #337ab7 +} + +a.bg-primary:hover { + background-color: #286090 +} + +.bg-success { + background-color: #dff0d8 +} + +a.bg-success:hover { + background-color: #c1e2b3 +} + +.bg-info { + background-color: #d9edf7 +} + +a.bg-info:hover { + background-color: #afd9ee +} + +.bg-warning { + background-color: #fcf8e3 +} + +a.bg-warning:hover { + background-color: #f7ecb5 +} + +.bg-danger { + background-color: #f2dede +} + +a.bg-danger:hover { + background-color: #e4b9b9 +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee +} + +ol, ul { + margin-top: 0; + margin-bottom: 10px +} + +ol ol, ol ul, ul ol, ul ul { + margin-bottom: 0 +} + +.list-unstyled { + padding-left: 0; + list-style: none +} + +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none +} + +.list-inline>li { + display: inline-block; + padding-right: 5px; + padding-left: 5px +} + +dl { + margin-top: 0; + margin-bottom: 20px +} + +dd, dt { + line-height: 1.42857143 +} + +dt { + font-weight: 700 +} + +dd { + margin-left: 0 +} + +@media ( min-width :768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap + } + .dl-horizontal dd { + margin-left: 180px + } +} + +abbr[data-original-title], abbr[title] { + cursor: help; + border-bottom: 1px dotted #777 +} + +.initialism { + font-size: 90%; + text-transform: uppercase +} + +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee +} + +blockquote ol:last-child, blockquote p:last-child, blockquote ul:last-child + { + margin-bottom: 0 +} + +blockquote .small, blockquote footer, blockquote small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777 +} + +blockquote .small:before, blockquote footer:before, blockquote small:before + { + content: '\2014 \00A0' +} + +.blockquote-reverse, blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0 +} + +.blockquote-reverse .small:before, .blockquote-reverse footer:before, + .blockquote-reverse small:before, blockquote.pull-right .small:before, + blockquote.pull-right footer:before, blockquote.pull-right small:before + { + content: '' +} + +.blockquote-reverse .small:after, .blockquote-reverse footer:after, + .blockquote-reverse small:after, blockquote.pull-right .small:after, + blockquote.pull-right footer:after, blockquote.pull-right small:after { + content: '\00A0 \2014' +} + +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143 +} + +code, kbd, pre, samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace +} + +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px +} + +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25) +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; + -webkit-box-shadow: none; + box-shadow: none +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px +} + +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0 +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll +} + +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +@media ( min-width :768px) { + .container { + width: 750px + } +} + +@media ( min-width :992px) { + .container { + width: 970px + } +} + +@media ( min-width :1200px) { + .container { + width: 1170px + } +} + +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +.row { + margin-right: -15px; + margin-left: -15px +} + +.col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, + .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, + .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, + .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, + .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, + .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, + .col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, + .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px +} + +.col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, + .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { + float: left +} + +.col-xs-12 { + width: 100% +} + +.col-xs-11 { + width: 91.66666667% +} + +.col-xs-10 { + width: 83.33333333% +} + +.col-xs-9 { + width: 75% +} + +.col-xs-8 { + width: 66.66666667% +} + +.col-xs-7 { + width: 58.33333333% +} + +.col-xs-6 { + width: 50% +} + +.col-xs-5 { + width: 41.66666667% +} + +.col-xs-4 { + width: 33.33333333% +} + +.col-xs-3 { + width: 25% +} + +.col-xs-2 { + width: 16.66666667% +} + +.col-xs-1 { + width: 8.33333333% +} + +.col-xs-pull-12 { + right: 100% +} + +.col-xs-pull-11 { + right: 91.66666667% +} + +.col-xs-pull-10 { + right: 83.33333333% +} + +.col-xs-pull-9 { + right: 75% +} + +.col-xs-pull-8 { + right: 66.66666667% +} + +.col-xs-pull-7 { + right: 58.33333333% +} + +.col-xs-pull-6 { + right: 50% +} + +.col-xs-pull-5 { + right: 41.66666667% +} + +.col-xs-pull-4 { + right: 33.33333333% +} + +.col-xs-pull-3 { + right: 25% +} + +.col-xs-pull-2 { + right: 16.66666667% +} + +.col-xs-pull-1 { + right: 8.33333333% +} + +.col-xs-pull-0 { + right: auto +} + +.col-xs-push-12 { + left: 100% +} + +.col-xs-push-11 { + left: 91.66666667% +} + +.col-xs-push-10 { + left: 83.33333333% +} + +.col-xs-push-9 { + left: 75% +} + +.col-xs-push-8 { + left: 66.66666667% +} + +.col-xs-push-7 { + left: 58.33333333% +} + +.col-xs-push-6 { + left: 50% +} + +.col-xs-push-5 { + left: 41.66666667% +} + +.col-xs-push-4 { + left: 33.33333333% +} + +.col-xs-push-3 { + left: 25% +} + +.col-xs-push-2 { + left: 16.66666667% +} + +.col-xs-push-1 { + left: 8.33333333% +} + +.col-xs-push-0 { + left: auto +} + +.col-xs-offset-12 { + margin-left: 100% +} + +.col-xs-offset-11 { + margin-left: 91.66666667% +} + +.col-xs-offset-10 { + margin-left: 83.33333333% +} + +.col-xs-offset-9 { + margin-left: 75% +} + +.col-xs-offset-8 { + margin-left: 66.66666667% +} + +.col-xs-offset-7 { + margin-left: 58.33333333% +} + +.col-xs-offset-6 { + margin-left: 50% +} + +.col-xs-offset-5 { + margin-left: 41.66666667% +} + +.col-xs-offset-4 { + margin-left: 33.33333333% +} + +.col-xs-offset-3 { + margin-left: 25% +} + +.col-xs-offset-2 { + margin-left: 16.66666667% +} + +.col-xs-offset-1 { + margin-left: 8.33333333% +} + +.col-xs-offset-0 { + margin-left: 0 +} + +@media ( min-width :768px) { + .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, + .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9 { + float: left + } + .col-sm-12 { + width: 100% + } + .col-sm-11 { + width: 91.66666667% + } + .col-sm-10 { + width: 83.33333333% + } + .col-sm-9 { + width: 75% + } + .col-sm-8 { + width: 66.66666667% + } + .col-sm-7 { + width: 58.33333333% + } + .col-sm-6 { + width: 50% + } + .col-sm-5 { + width: 41.66666667% + } + .col-sm-4 { + width: 33.33333333% + } + .col-sm-3 { + width: 25% + } + .col-sm-2 { + width: 16.66666667% + } + .col-sm-1 { + width: 8.33333333% + } + .col-sm-pull-12 { + right: 100% + } + .col-sm-pull-11 { + right: 91.66666667% + } + .col-sm-pull-10 { + right: 83.33333333% + } + .col-sm-pull-9 { + right: 75% + } + .col-sm-pull-8 { + right: 66.66666667% + } + .col-sm-pull-7 { + right: 58.33333333% + } + .col-sm-pull-6 { + right: 50% + } + .col-sm-pull-5 { + right: 41.66666667% + } + .col-sm-pull-4 { + right: 33.33333333% + } + .col-sm-pull-3 { + right: 25% + } + .col-sm-pull-2 { + right: 16.66666667% + } + .col-sm-pull-1 { + right: 8.33333333% + } + .col-sm-pull-0 { + right: auto + } + .col-sm-push-12 { + left: 100% + } + .col-sm-push-11 { + left: 91.66666667% + } + .col-sm-push-10 { + left: 83.33333333% + } + .col-sm-push-9 { + left: 75% + } + .col-sm-push-8 { + left: 66.66666667% + } + .col-sm-push-7 { + left: 58.33333333% + } + .col-sm-push-6 { + left: 50% + } + .col-sm-push-5 { + left: 41.66666667% + } + .col-sm-push-4 { + left: 33.33333333% + } + .col-sm-push-3 { + left: 25% + } + .col-sm-push-2 { + left: 16.66666667% + } + .col-sm-push-1 { + left: 8.33333333% + } + .col-sm-push-0 { + left: auto + } + .col-sm-offset-12 { + margin-left: 100% + } + .col-sm-offset-11 { + margin-left: 91.66666667% + } + .col-sm-offset-10 { + margin-left: 83.33333333% + } + .col-sm-offset-9 { + margin-left: 75% + } + .col-sm-offset-8 { + margin-left: 66.66666667% + } + .col-sm-offset-7 { + margin-left: 58.33333333% + } + .col-sm-offset-6 { + margin-left: 50% + } + .col-sm-offset-5 { + margin-left: 41.66666667% + } + .col-sm-offset-4 { + margin-left: 33.33333333% + } + .col-sm-offset-3 { + margin-left: 25% + } + .col-sm-offset-2 { + margin-left: 16.66666667% + } + .col-sm-offset-1 { + margin-left: 8.33333333% + } + .col-sm-offset-0 { + margin-left: 0 + } +} + +@media ( min-width :992px) { + .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, + .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9 { + float: left + } + .col-md-12 { + width: 100% + } + .col-md-11 { + width: 91.66666667% + } + .col-md-10 { + width: 83.33333333% + } + .col-md-9 { + width: 75% + } + .col-md-8 { + width: 66.66666667% + } + .col-md-7 { + width: 58.33333333% + } + .col-md-6 { + width: 50% + } + .col-md-5 { + width: 41.66666667% + } + .col-md-4 { + width: 33.33333333% + } + .col-md-3 { + width: 25% + } + .col-md-2 { + width: 16.66666667% + } + .col-md-1 { + width: 8.33333333% + } + .col-md-pull-12 { + right: 100% + } + .col-md-pull-11 { + right: 91.66666667% + } + .col-md-pull-10 { + right: 83.33333333% + } + .col-md-pull-9 { + right: 75% + } + .col-md-pull-8 { + right: 66.66666667% + } + .col-md-pull-7 { + right: 58.33333333% + } + .col-md-pull-6 { + right: 50% + } + .col-md-pull-5 { + right: 41.66666667% + } + .col-md-pull-4 { + right: 33.33333333% + } + .col-md-pull-3 { + right: 25% + } + .col-md-pull-2 { + right: 16.66666667% + } + .col-md-pull-1 { + right: 8.33333333% + } + .col-md-pull-0 { + right: auto + } + .col-md-push-12 { + left: 100% + } + .col-md-push-11 { + left: 91.66666667% + } + .col-md-push-10 { + left: 83.33333333% + } + .col-md-push-9 { + left: 75% + } + .col-md-push-8 { + left: 66.66666667% + } + .col-md-push-7 { + left: 58.33333333% + } + .col-md-push-6 { + left: 50% + } + .col-md-push-5 { + left: 41.66666667% + } + .col-md-push-4 { + left: 33.33333333% + } + .col-md-push-3 { + left: 25% + } + .col-md-push-2 { + left: 16.66666667% + } + .col-md-push-1 { + left: 8.33333333% + } + .col-md-push-0 { + left: auto + } + .col-md-offset-12 { + margin-left: 100% + } + .col-md-offset-11 { + margin-left: 91.66666667% + } + .col-md-offset-10 { + margin-left: 83.33333333% + } + .col-md-offset-9 { + margin-left: 75% + } + .col-md-offset-8 { + margin-left: 66.66666667% + } + .col-md-offset-7 { + margin-left: 58.33333333% + } + .col-md-offset-6 { + margin-left: 50% + } + .col-md-offset-5 { + margin-left: 41.66666667% + } + .col-md-offset-4 { + margin-left: 33.33333333% + } + .col-md-offset-3 { + margin-left: 25% + } + .col-md-offset-2 { + margin-left: 16.66666667% + } + .col-md-offset-1 { + margin-left: 8.33333333% + } + .col-md-offset-0 { + margin-left: 0 + } +} + +@media ( min-width :1200px) { + .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, + .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9 { + float: left + } + .col-lg-12 { + width: 100% + } + .col-lg-11 { + width: 91.66666667% + } + .col-lg-10 { + width: 83.33333333% + } + .col-lg-9 { + width: 75% + } + .col-lg-8 { + width: 66.66666667% + } + .col-lg-7 { + width: 58.33333333% + } + .col-lg-6 { + width: 50% + } + .col-lg-5 { + width: 41.66666667% + } + .col-lg-4 { + width: 33.33333333% + } + .col-lg-3 { + width: 25% + } + .col-lg-2 { + width: 16.66666667% + } + .col-lg-1 { + width: 8.33333333% + } + .col-lg-pull-12 { + right: 100% + } + .col-lg-pull-11 { + right: 91.66666667% + } + .col-lg-pull-10 { + right: 83.33333333% + } + .col-lg-pull-9 { + right: 75% + } + .col-lg-pull-8 { + right: 66.66666667% + } + .col-lg-pull-7 { + right: 58.33333333% + } + .col-lg-pull-6 { + right: 50% + } + .col-lg-pull-5 { + right: 41.66666667% + } + .col-lg-pull-4 { + right: 33.33333333% + } + .col-lg-pull-3 { + right: 25% + } + .col-lg-pull-2 { + right: 16.66666667% + } + .col-lg-pull-1 { + right: 8.33333333% + } + .col-lg-pull-0 { + right: auto + } + .col-lg-push-12 { + left: 100% + } + .col-lg-push-11 { + left: 91.66666667% + } + .col-lg-push-10 { + left: 83.33333333% + } + .col-lg-push-9 { + left: 75% + } + .col-lg-push-8 { + left: 66.66666667% + } + .col-lg-push-7 { + left: 58.33333333% + } + .col-lg-push-6 { + left: 50% + } + .col-lg-push-5 { + left: 41.66666667% + } + .col-lg-push-4 { + left: 33.33333333% + } + .col-lg-push-3 { + left: 25% + } + .col-lg-push-2 { + left: 16.66666667% + } + .col-lg-push-1 { + left: 8.33333333% + } + .col-lg-push-0 { + left: auto + } + .col-lg-offset-12 { + margin-left: 100% + } + .col-lg-offset-11 { + margin-left: 91.66666667% + } + .col-lg-offset-10 { + margin-left: 83.33333333% + } + .col-lg-offset-9 { + margin-left: 75% + } + .col-lg-offset-8 { + margin-left: 66.66666667% + } + .col-lg-offset-7 { + margin-left: 58.33333333% + } + .col-lg-offset-6 { + margin-left: 50% + } + .col-lg-offset-5 { + margin-left: 41.66666667% + } + .col-lg-offset-4 { + margin-left: 33.33333333% + } + .col-lg-offset-3 { + margin-left: 25% + } + .col-lg-offset-2 { + margin-left: 16.66666667% + } + .col-lg-offset-1 { + margin-left: 8.33333333% + } + .col-lg-offset-0 { + margin-left: 0 + } +} + +table { + background-color: transparent +} + +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left +} + +th { + text-align: left +} + +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px +} + +.table>tbody>tr>td, .table>tbody>tr>th, .table>tfoot>tr>td, .table>tfoot>tr>th, + .table>thead>tr>td, .table>thead>tr>th { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd +} + +.table>thead>tr>th { + vertical-align: bottom; + border-bottom: 2px solid #ddd +} + +.table>caption+thead>tr:first-child>td, .table>caption+thead>tr:first-child>th, + .table>colgroup+thead>tr:first-child>td, .table>colgroup+thead>tr:first-child>th, + .table>thead:first-child>tr:first-child>td, .table>thead:first-child>tr:first-child>th + { + border-top: 0 +} + +.table>tbody+tbody { + border-top: 2px solid #ddd +} + +.table .table { + background-color: #fff +} + +.table-condensed>tbody>tr>td, .table-condensed>tbody>tr>th, + .table-condensed>tfoot>tr>td, .table-condensed>tfoot>tr>th, + .table-condensed>thead>tr>td, .table-condensed>thead>tr>th { + padding: 5px +} + +.table-bordered { + border: 1px solid #ddd +} + +.table-bordered>tbody>tr>td, .table-bordered>tbody>tr>th, + .table-bordered>tfoot>tr>td, .table-bordered>tfoot>tr>th, + .table-bordered>thead>tr>td, .table-bordered>thead>tr>th { + border: 1px solid #ddd +} + +.table-bordered>thead>tr>td, .table-bordered>thead>tr>th { + border-bottom-width: 2px +} + +.table-striped>tbody>tr:nth-of-type(odd) { + background-color: #f9f9f9 +} + +.table-hover>tbody>tr:hover { + background-color: #f5f5f5 +} + +table col[class*=col-] { + position: static; + display: table-column; + float: none +} + +table td[class*=col-], table th[class*=col-] { + position: static; + display: table-cell; + float: none +} + +.table>tbody>tr.active>td, .table>tbody>tr.active>th, .table>tbody>tr>td.active, + .table>tbody>tr>th.active, .table>tfoot>tr.active>td, .table>tfoot>tr.active>th, + .table>tfoot>tr>td.active, .table>tfoot>tr>th.active, .table>thead>tr.active>td, + .table>thead>tr.active>th, .table>thead>tr>td.active, .table>thead>tr>th.active + { + background-color: #f5f5f5 +} + +.table-hover>tbody>tr.active:hover>td, .table-hover>tbody>tr.active:hover>th, + .table-hover>tbody>tr:hover>.active, .table-hover>tbody>tr>td.active:hover, + .table-hover>tbody>tr>th.active:hover { + background-color: #e8e8e8 +} + +.table>tbody>tr.success>td, .table>tbody>tr.success>th, .table>tbody>tr>td.success, + .table>tbody>tr>th.success, .table>tfoot>tr.success>td, .table>tfoot>tr.success>th, + .table>tfoot>tr>td.success, .table>tfoot>tr>th.success, .table>thead>tr.success>td, + .table>thead>tr.success>th, .table>thead>tr>td.success, .table>thead>tr>th.success + { + background-color: #dff0d8 +} + +.table-hover>tbody>tr.success:hover>td, .table-hover>tbody>tr.success:hover>th, + .table-hover>tbody>tr:hover>.success, .table-hover>tbody>tr>td.success:hover, + .table-hover>tbody>tr>th.success:hover { + background-color: #d0e9c6 +} + +.table>tbody>tr.info>td, .table>tbody>tr.info>th, .table>tbody>tr>td.info, + .table>tbody>tr>th.info, .table>tfoot>tr.info>td, .table>tfoot>tr.info>th, + .table>tfoot>tr>td.info, .table>tfoot>tr>th.info, .table>thead>tr.info>td, + .table>thead>tr.info>th, .table>thead>tr>td.info, .table>thead>tr>th.info + { + background-color: #d9edf7 +} + +.table-hover>tbody>tr.info:hover>td, .table-hover>tbody>tr.info:hover>th, + .table-hover>tbody>tr:hover>.info, .table-hover>tbody>tr>td.info:hover, + .table-hover>tbody>tr>th.info:hover { + background-color: #c4e3f3 +} + +.table>tbody>tr.warning>td, .table>tbody>tr.warning>th, .table>tbody>tr>td.warning, + .table>tbody>tr>th.warning, .table>tfoot>tr.warning>td, .table>tfoot>tr.warning>th, + .table>tfoot>tr>td.warning, .table>tfoot>tr>th.warning, .table>thead>tr.warning>td, + .table>thead>tr.warning>th, .table>thead>tr>td.warning, .table>thead>tr>th.warning + { + background-color: #fcf8e3 +} + +.table-hover>tbody>tr.warning:hover>td, .table-hover>tbody>tr.warning:hover>th, + .table-hover>tbody>tr:hover>.warning, .table-hover>tbody>tr>td.warning:hover, + .table-hover>tbody>tr>th.warning:hover { + background-color: #faf2cc +} + +.table>tbody>tr.danger>td, .table>tbody>tr.danger>th, .table>tbody>tr>td.danger, + .table>tbody>tr>th.danger, .table>tfoot>tr.danger>td, .table>tfoot>tr.danger>th, + .table>tfoot>tr>td.danger, .table>tfoot>tr>th.danger, .table>thead>tr.danger>td, + .table>thead>tr.danger>th, .table>thead>tr>td.danger, .table>thead>tr>th.danger + { + background-color: #f2dede +} + +.table-hover>tbody>tr.danger:hover>td, .table-hover>tbody>tr.danger:hover>th, + .table-hover>tbody>tr:hover>.danger, .table-hover>tbody>tr>td.danger:hover, + .table-hover>tbody>tr>th.danger:hover { + background-color: #ebcccc +} + +.table-responsive { + min-height: .01%; + overflow-x: auto +} + +@media screen and (max-width:767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd + } + .table-responsive>.table { + margin-bottom: 0 + } + .table-responsive>.table>tbody>tr>td, .table-responsive>.table>tbody>tr>th, + .table-responsive>.table>tfoot>tr>td, .table-responsive>.table>tfoot>tr>th, + .table-responsive>.table>thead>tr>td, .table-responsive>.table>thead>tr>th + { + white-space: nowrap + } + .table-responsive>.table-bordered { + border: 0 + } + .table-responsive>.table-bordered>tbody>tr>td:first-child, + .table-responsive>.table-bordered>tbody>tr>th:first-child, + .table-responsive>.table-bordered>tfoot>tr>td:first-child, + .table-responsive>.table-bordered>tfoot>tr>th:first-child, + .table-responsive>.table-bordered>thead>tr>td:first-child, + .table-responsive>.table-bordered>thead>tr>th:first-child { + border-left: 0 + } + .table-responsive>.table-bordered>tbody>tr>td:last-child, + .table-responsive>.table-bordered>tbody>tr>th:last-child, + .table-responsive>.table-bordered>tfoot>tr>td:last-child, + .table-responsive>.table-bordered>tfoot>tr>th:last-child, + .table-responsive>.table-bordered>thead>tr>td:last-child, + .table-responsive>.table-bordered>thead>tr>th:last-child { + border-right: 0 + } + .table-responsive>.table-bordered>tbody>tr:last-child>td, + .table-responsive>.table-bordered>tbody>tr:last-child>th, + .table-responsive>.table-bordered>tfoot>tr:last-child>td, + .table-responsive>.table-bordered>tfoot>tr:last-child>th { + border-bottom: 0 + } +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0 +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5 +} + +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: 700 +} + +input[type=search] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box +} + +input[type=checkbox], input[type=radio] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal +} + +input[type=file] { + display: block +} + +input[type=range] { + display: block; + width: 100% +} + +select[multiple], select[size] { + height: auto +} + +input[type=file]:focus, input[type=checkbox]:focus, input[type=radio]:focus + { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555 +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow + ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out + .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px + rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px + rgba(102, 175, 233, .6) +} + +.form-control::-moz-placeholder { + color: #999; + opacity: 1 +} + +.form-control:-ms-input-placeholder { + color: #999 +} + +.form-control::-webkit-input-placeholder { + color: #999 +} + +.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control + { + background-color: #eee; + opacity: 1 +} + +.form-control[disabled], fieldset[disabled] .form-control { + cursor: not-allowed +} + +textarea.form-control { + height: auto +} + +input[type=search] { + -webkit-appearance: none +} + +@media screen and (-webkit-min-device-pixel-ratio:0) { + input[type=date], input[type=time], input[type=datetime-local], input[type=month] + { + line-height: 34px + } + .input-group-sm input[type=date], .input-group-sm input[type=time], + .input-group-sm input[type=datetime-local], .input-group-sm input[type=month], + input[type=date].input-sm, input[type=time].input-sm, input[type=datetime-local].input-sm, + input[type=month].input-sm { + line-height: 30px + } + .input-group-lg input[type=date], .input-group-lg input[type=time], + .input-group-lg input[type=datetime-local], .input-group-lg input[type=month], + input[type=date].input-lg, input[type=time].input-lg, input[type=datetime-local].input-lg, + input[type=month].input-lg { + line-height: 46px + } +} + +.form-group { + margin-bottom: 15px +} + +.checkbox, .radio { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px +} + +.checkbox label, .radio label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + cursor: pointer +} + +.checkbox input[type=checkbox], .checkbox-inline input[type=checkbox], + .radio input[type=radio], .radio-inline input[type=radio] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px +} + +.checkbox+.checkbox, .radio+.radio { + margin-top: -5px +} + +.checkbox-inline, .radio-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: 400; + vertical-align: middle; + cursor: pointer +} + +.checkbox-inline+.checkbox-inline, .radio-inline+.radio-inline { + margin-top: 0; + margin-left: 10px +} + +fieldset[disabled] input[type=checkbox], fieldset[disabled] input[type=radio], + input[type=checkbox].disabled, input[type=checkbox][disabled], input[type=radio].disabled, + input[type=radio][disabled] { + cursor: not-allowed +} + +.checkbox-inline.disabled, .radio-inline.disabled, fieldset[disabled] .checkbox-inline, + fieldset[disabled] .radio-inline { + cursor: not-allowed +} + +.checkbox.disabled label, .radio.disabled label, fieldset[disabled] .checkbox label, + fieldset[disabled] .radio label { + cursor: not-allowed +} + +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0 +} + +.form-control-static.input-lg, .form-control-static.input-sm { + padding-right: 0; + padding-left: 0 +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-sm { + height: 30px; + line-height: 30px +} + +select[multiple].input-sm, textarea.input-sm { + height: auto +} + +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.form-group-sm .form-control { + height: 30px; + line-height: 30px +} + +select[multiple].form-group-sm .form-control, textarea.form-group-sm .form-control + { + height: auto +} + +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5 +} + +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.input-lg { + height: 46px; + line-height: 46px +} + +select[multiple].input-lg, textarea.input-lg { + height: auto +} + +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.form-group-lg .form-control { + height: 46px; + line-height: 46px +} + +select[multiple].form-group-lg .form-control, textarea.form-group-lg .form-control + { + height: auto +} + +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333 +} + +.has-feedback { + position: relative +} + +.has-feedback .form-control { + padding-right: 42.5px +} + +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none +} + +.input-lg+.form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px +} + +.input-sm+.form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px +} + +.has-success .checkbox, .has-success .checkbox-inline, .has-success .control-label, + .has-success .help-block, .has-success .radio, .has-success .radio-inline, + .has-success.checkbox label, .has-success.checkbox-inline label, + .has-success.radio label, .has-success.radio-inline label { + color: #3c763d +} + +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) +} + +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168 +} + +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d +} + +.has-success .form-control-feedback { + color: #3c763d +} + +.has-warning .checkbox, .has-warning .checkbox-inline, .has-warning .control-label, + .has-warning .help-block, .has-warning .radio, .has-warning .radio-inline, + .has-warning.checkbox label, .has-warning.checkbox-inline label, + .has-warning.radio label, .has-warning.radio-inline label { + color: #8a6d3b +} + +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) +} + +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b +} + +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b +} + +.has-warning .form-control-feedback { + color: #8a6d3b +} + +.has-error .checkbox, .has-error .checkbox-inline, .has-error .control-label, + .has-error .help-block, .has-error .radio, .has-error .radio-inline, + .has-error.checkbox label, .has-error.checkbox-inline label, .has-error.radio label, + .has-error.radio-inline label { + color: #a94442 +} + +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075) +} + +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483 +} + +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442 +} + +.has-error .form-control-feedback { + color: #a94442 +} + +.has-feedback label ~.form-control-feedback { + top: 25px +} + +.has-feedback label.sr-only ~.form-control-feedback { + top: 0 +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373 +} + +@media ( min-width :768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + .form-inline .form-control-static { + display: inline-block + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle + } + .form-inline .input-group .form-control, .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn { + width: auto + } + .form-inline .input-group>.form-control { + width: 100% + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle + } + .form-inline .checkbox, .form-inline .radio { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + .form-inline .checkbox label, .form-inline .radio label { + padding-left: 0 + } + .form-inline .checkbox input[type=checkbox], .form-inline .radio input[type=radio] + { + position: relative; + margin-left: 0 + } + .form-inline .has-feedback .form-control-feedback { + top: 0 + } +} + +.form-horizontal .checkbox, .form-horizontal .checkbox-inline, + .form-horizontal .radio, .form-horizontal .radio-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0 +} + +.form-horizontal .checkbox, .form-horizontal .radio { + min-height: 27px +} + +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px +} + +@media ( min-width :768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right + } +} + +.form-horizontal .has-feedback .form-control-feedback { + right: 15px +} + +@media ( min-width :768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.33px + } +} + +@media ( min-width :768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px + } +} + +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px +} + +.btn.active.focus, .btn.active:focus, .btn.focus, .btn:active.focus, + .btn:active:focus, .btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px +} + +.btn.focus, .btn:focus, .btn:hover { + color: #333; + text-decoration: none +} + +.btn.active, .btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125) +} + +.btn.disabled, .btn[disabled], fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity = 65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65 +} + +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc +} + +.btn-default.active, .btn-default.focus, .btn-default:active, + .btn-default:focus, .btn-default:hover, .open>.dropdown-toggle.btn-default + { + color: #333; + background-color: #e6e6e6; + border-color: #adadad +} + +.btn-default.active, .btn-default:active, .open>.dropdown-toggle.btn-default + { + background-image: none +} + +.btn-default.disabled, .btn-default.disabled.active, .btn-default.disabled.focus, + .btn-default.disabled:active, .btn-default.disabled:focus, .btn-default.disabled:hover, + .btn-default[disabled], .btn-default[disabled].active, .btn-default[disabled].focus, + .btn-default[disabled]:active, .btn-default[disabled]:focus, + .btn-default[disabled]:hover, fieldset[disabled] .btn-default, fieldset[disabled] .btn-default.active, + fieldset[disabled] .btn-default.focus, fieldset[disabled] .btn-default:active, + fieldset[disabled] .btn-default:focus, fieldset[disabled] .btn-default:hover + { + background-color: #fff; + border-color: #ccc +} + +.btn-default .badge { + color: #fff; + background-color: #333 +} + +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4 +} + +.btn-primary.active, .btn-primary.focus, .btn-primary:active, + .btn-primary:focus, .btn-primary:hover, .open>.dropdown-toggle.btn-primary + { + color: #fff; + background-color: #286090; + border-color: #204d74 +} + +.btn-primary.active, .btn-primary:active, .open>.dropdown-toggle.btn-primary + { + background-image: none +} + +.btn-primary.disabled, .btn-primary.disabled.active, .btn-primary.disabled.focus, + .btn-primary.disabled:active, .btn-primary.disabled:focus, .btn-primary.disabled:hover, + .btn-primary[disabled], .btn-primary[disabled].active, .btn-primary[disabled].focus, + .btn-primary[disabled]:active, .btn-primary[disabled]:focus, + .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary, fieldset[disabled] .btn-primary.active, + fieldset[disabled] .btn-primary.focus, fieldset[disabled] .btn-primary:active, + fieldset[disabled] .btn-primary:focus, fieldset[disabled] .btn-primary:hover + { + background-color: #337ab7; + border-color: #2e6da4 +} + +.btn-primary .badge { + color: #337ab7; + background-color: #fff +} + +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c +} + +.btn-success.active, .btn-success.focus, .btn-success:active, + .btn-success:focus, .btn-success:hover, .open>.dropdown-toggle.btn-success + { + color: #fff; + background-color: #449d44; + border-color: #398439 +} + +.btn-success.active, .btn-success:active, .open>.dropdown-toggle.btn-success + { + background-image: none +} + +.btn-success.disabled, .btn-success.disabled.active, .btn-success.disabled.focus, + .btn-success.disabled:active, .btn-success.disabled:focus, .btn-success.disabled:hover, + .btn-success[disabled], .btn-success[disabled].active, .btn-success[disabled].focus, + .btn-success[disabled]:active, .btn-success[disabled]:focus, + .btn-success[disabled]:hover, fieldset[disabled] .btn-success, fieldset[disabled] .btn-success.active, + fieldset[disabled] .btn-success.focus, fieldset[disabled] .btn-success:active, + fieldset[disabled] .btn-success:focus, fieldset[disabled] .btn-success:hover + { + background-color: #5cb85c; + border-color: #4cae4c +} + +.btn-success .badge { + color: #5cb85c; + background-color: #fff +} + +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da +} + +.btn-info.active, .btn-info.focus, .btn-info:active, .btn-info:focus, + .btn-info:hover, .open>.dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc +} + +.btn-info.active, .btn-info:active, .open>.dropdown-toggle.btn-info { + background-image: none +} + +.btn-info.disabled, .btn-info.disabled.active, .btn-info.disabled.focus, + .btn-info.disabled:active, .btn-info.disabled:focus, .btn-info.disabled:hover, + .btn-info[disabled], .btn-info[disabled].active, .btn-info[disabled].focus, + .btn-info[disabled]:active, .btn-info[disabled]:focus, .btn-info[disabled]:hover, + fieldset[disabled] .btn-info, fieldset[disabled] .btn-info.active, + fieldset[disabled] .btn-info.focus, fieldset[disabled] .btn-info:active, + fieldset[disabled] .btn-info:focus, fieldset[disabled] .btn-info:hover + { + background-color: #5bc0de; + border-color: #46b8da +} + +.btn-info .badge { + color: #5bc0de; + background-color: #fff +} + +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236 +} + +.btn-warning.active, .btn-warning.focus, .btn-warning:active, + .btn-warning:focus, .btn-warning:hover, .open>.dropdown-toggle.btn-warning + { + color: #fff; + background-color: #ec971f; + border-color: #d58512 +} + +.btn-warning.active, .btn-warning:active, .open>.dropdown-toggle.btn-warning + { + background-image: none +} + +.btn-warning.disabled, .btn-warning.disabled.active, .btn-warning.disabled.focus, + .btn-warning.disabled:active, .btn-warning.disabled:focus, .btn-warning.disabled:hover, + .btn-warning[disabled], .btn-warning[disabled].active, .btn-warning[disabled].focus, + .btn-warning[disabled]:active, .btn-warning[disabled]:focus, + .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning, fieldset[disabled] .btn-warning.active, + fieldset[disabled] .btn-warning.focus, fieldset[disabled] .btn-warning:active, + fieldset[disabled] .btn-warning:focus, fieldset[disabled] .btn-warning:hover + { + background-color: #f0ad4e; + border-color: #eea236 +} + +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff +} + +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a +} + +.btn-danger.active, .btn-danger.focus, .btn-danger:active, .btn-danger:focus, + .btn-danger:hover, .open>.dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925 +} + +.btn-danger.active, .btn-danger:active, .open>.dropdown-toggle.btn-danger + { + background-image: none +} + +.btn-danger.disabled, .btn-danger.disabled.active, .btn-danger.disabled.focus, + .btn-danger.disabled:active, .btn-danger.disabled:focus, .btn-danger.disabled:hover, + .btn-danger[disabled], .btn-danger[disabled].active, .btn-danger[disabled].focus, + .btn-danger[disabled]:active, .btn-danger[disabled]:focus, .btn-danger[disabled]:hover, + fieldset[disabled] .btn-danger, fieldset[disabled] .btn-danger.active, + fieldset[disabled] .btn-danger.focus, fieldset[disabled] .btn-danger:active, + fieldset[disabled] .btn-danger:focus, fieldset[disabled] .btn-danger:hover + { + background-color: #d9534f; + border-color: #d43f3a +} + +.btn-danger .badge { + color: #d9534f; + background-color: #fff +} + +.btn-link { + font-weight: 400; + color: #337ab7; + border-radius: 0 +} + +.btn-link, .btn-link.active, .btn-link:active, .btn-link[disabled], + fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none +} + +.btn-link, .btn-link:active, .btn-link:focus, .btn-link:hover { + border-color: transparent +} + +.btn-link:focus, .btn-link:hover { + color: #23527c; + text-decoration: underline; + background-color: transparent +} + +.btn-link[disabled]:focus, .btn-link[disabled]:hover, fieldset[disabled] .btn-link:focus, + fieldset[disabled] .btn-link:hover { + color: #777; + text-decoration: none +} + +.btn-group-lg>.btn, .btn-lg { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +.btn-group-sm>.btn, .btn-sm { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-group-xs>.btn, .btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +.btn-block { + display: block; + width: 100% +} + +.btn-block+.btn-block { + margin-top: 5px +} + +input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].btn-block + { + width: 100% +} + +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear +} + +.fade.in { + opacity: 1 +} + +.collapse { + display: none +} + +.collapse.in { + display: block +} + +tr.collapse.in { + display: table-row +} + +tbody.collapse.in { + display: table-row-group +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility +} + +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-right: 4px solid transparent; + border-left: 4px solid transparent +} + +.dropdown, .dropup { + position: relative +} + +.dropdown-toggle:focus { + outline: 0 +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175) +} + +.dropdown-menu.pull-right { + right: 0; + left: auto +} + +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5 +} + +.dropdown-menu>li>a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap +} + +.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover { + color: #262626; + text-decoration: none; + background-color: #f5f5f5 +} + +.dropdown-menu>.active>a, .dropdown-menu>.active>a:focus, .dropdown-menu>.active>a:hover + { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0 +} + +.dropdown-menu>.disabled>a, .dropdown-menu>.disabled>a:focus, + .dropdown-menu>.disabled>a:hover { + color: #777 +} + +.dropdown-menu>.disabled>a:focus, .dropdown-menu>.disabled>a:hover { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false) +} + +.open>.dropdown-menu { + display: block +} + +.open>a { + outline: 0 +} + +.dropdown-menu-right { + right: 0; + left: auto +} + +.dropdown-menu-left { + right: auto; + left: 0 +} + +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990 +} + +.pull-right>.dropdown-menu { + right: 0; + left: auto +} + +.dropup .caret, .navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid +} + +.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px +} + +@media ( min-width :768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0 + } +} + +.btn-group, .btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle +} + +.btn-group-vertical>.btn, .btn-group>.btn { + position: relative; + float: left +} + +.btn-group-vertical>.btn.active, .btn-group-vertical>.btn:active, + .btn-group-vertical>.btn:focus, .btn-group-vertical>.btn:hover, + .btn-group>.btn.active, .btn-group>.btn:active, .btn-group>.btn:focus, + .btn-group>.btn:hover { + z-index: 2 +} + +.btn-group .btn+.btn, .btn-group .btn+.btn-group, .btn-group .btn-group+.btn, + .btn-group .btn-group+.btn-group { + margin-left: -1px +} + +.btn-toolbar { + margin-left: -5px +} + +.btn-toolbar .btn-group, .btn-toolbar .input-group { + float: left +} + +.btn-toolbar>.btn, .btn-toolbar>.btn-group, .btn-toolbar>.input-group { + margin-left: 5px +} + +.btn-group>.btn:not (:first-child ):not (:last-child ):not (.dropdown-toggle + ){ + border-radius: 0 +} + +.btn-group>.btn:first-child { + margin-left: 0 +} + +.btn-group>.btn:first-child:not (:last-child ):not (.dropdown-toggle ){ + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-group>.btn:last-child:not (:first-child ), .btn-group>.dropdown-toggle:not + (:first-child ){ + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group>.btn-group { + float: left +} + +.btn-group>.btn-group:not (:first-child ):not (:last-child )>.btn { + border-radius: 0 +} + +.btn-group>.btn-group:first-child:not (:last-child )>.btn:last-child, + .btn-group>.btn-group:first-child:not (:last-child )>.dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-group>.btn-group:last-child:not (:first-child )>.btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { + outline: 0 +} + +.btn-group>.btn+.dropdown-toggle { + padding-right: 8px; + padding-left: 8px +} + +.btn-group>.btn-lg+.dropdown-toggle { + padding-right: 12px; + padding-left: 12px +} + +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125) +} + +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none +} + +.btn .caret { + margin-left: 0 +} + +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0 +} + +.dropup .btn-lg .caret { + border-width: 0 5px 5px +} + +.btn-group-vertical>.btn, .btn-group-vertical>.btn-group, + .btn-group-vertical>.btn-group>.btn { + display: block; + float: none; + width: 100%; + max-width: 100% +} + +.btn-group-vertical>.btn-group>.btn { + float: none +} + +.btn-group-vertical>.btn+.btn, .btn-group-vertical>.btn+.btn-group, + .btn-group-vertical>.btn-group+.btn, .btn-group-vertical>.btn-group+.btn-group + { + margin-top: -1px; + margin-left: 0 +} + +.btn-group-vertical>.btn:not (:first-child ):not (:last-child ){ + border-radius: 0 +} + +.btn-group-vertical>.btn:first-child:not (:last-child ){ + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical>.btn:last-child:not (:first-child ){ + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px +} + +.btn-group-vertical>.btn-group:not (:first-child ):not (:last-child )>.btn + { + border-radius: 0 +} + +.btn-group-vertical>.btn-group:first-child:not (:last-child )>.btn:last-child, + .btn-group-vertical>.btn-group:first-child:not (:last-child )>.dropdown-toggle + { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical>.btn-group:last-child:not (:first-child )>.btn:first-child + { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate +} + +.btn-group-justified>.btn, .btn-group-justified>.btn-group { + display: table-cell; + float: none; + width: 1% +} + +.btn-group-justified>.btn-group .btn { + width: 100% +} + +.btn-group-justified>.btn-group .dropdown-menu { + left: auto +} + +[data-toggle=buttons]>.btn input[type=checkbox], [data-toggle=buttons]>.btn input[type=radio], + [data-toggle=buttons]>.btn-group>.btn input[type=checkbox], [data-toggle=buttons]>.btn-group>.btn input[type=radio] + { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none +} + +.input-group { + position: relative; + display: table; + border-collapse: separate +} + +.input-group[class*=col-] { + float: none; + padding-right: 0; + padding-left: 0 +} + +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0 +} + +.input-group-lg>.form-control, .input-group-lg>.input-group-addon, + .input-group-lg>.input-group-btn>.btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px +} + +select.input-group-lg>.form-control, select.input-group-lg>.input-group-addon, + select.input-group-lg>.input-group-btn>.btn { + height: 46px; + line-height: 46px +} + +select[multiple].input-group-lg>.form-control, select[multiple].input-group-lg>.input-group-addon, + select[multiple].input-group-lg>.input-group-btn>.btn, textarea.input-group-lg>.form-control, + textarea.input-group-lg>.input-group-addon, textarea.input-group-lg>.input-group-btn>.btn + { + height: auto +} + +.input-group-sm>.form-control, .input-group-sm>.input-group-addon, + .input-group-sm>.input-group-btn>.btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px +} + +select.input-group-sm>.form-control, select.input-group-sm>.input-group-addon, + select.input-group-sm>.input-group-btn>.btn { + height: 30px; + line-height: 30px +} + +select[multiple].input-group-sm>.form-control, select[multiple].input-group-sm>.input-group-addon, + select[multiple].input-group-sm>.input-group-btn>.btn, textarea.input-group-sm>.form-control, + textarea.input-group-sm>.input-group-addon, textarea.input-group-sm>.input-group-btn>.btn + { + height: auto +} + +.input-group .form-control, .input-group-addon, .input-group-btn { + display: table-cell +} + +.input-group .form-control:not (:first-child ):not (:last-child ), + .input-group-addon:not (:first-child ):not (:last-child ), + .input-group-btn:not (:first-child ):not (:last-child ){ + border-radius: 0 +} + +.input-group-addon, .input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: 400; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px +} + +.input-group-addon input[type=checkbox], .input-group-addon input[type=radio] + { + margin-top: 0 +} + +.input-group .form-control:first-child, .input-group-addon:first-child, + .input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group>.btn, + .input-group-btn:first-child>.dropdown-toggle, .input-group-btn:last-child>.btn-group:not + (:last-child )>.btn, .input-group-btn:last-child>.btn:not (:last-child + ):not (.dropdown-toggle ){ + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group-addon:first-child { + border-right: 0 +} + +.input-group .form-control:last-child, .input-group-addon:last-child, + .input-group-btn:first-child>.btn-group:not (:first-child )>.btn, + .input-group-btn:first-child>.btn:not (:first-child ), .input-group-btn:last-child>.btn, + .input-group-btn:last-child>.btn-group>.btn, .input-group-btn:last-child>.dropdown-toggle + { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group-addon:last-child { + border-left: 0 +} + +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap +} + +.input-group-btn>.btn { + position: relative +} + +.input-group-btn>.btn+.btn { + margin-left: -1px +} + +.input-group-btn>.btn:active, .input-group-btn>.btn:focus, + .input-group-btn>.btn:hover { + z-index: 2 +} + +.input-group-btn:first-child>.btn, .input-group-btn:first-child>.btn-group + { + margin-right: -1px +} + +.input-group-btn:last-child>.btn, .input-group-btn:last-child>.btn-group + { + margin-left: -1px +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.nav>li { + position: relative; + display: block +} + +.nav>li>a { + position: relative; + display: block; + padding: 10px 15px +} + +.nav>li>a:focus, .nav>li>a:hover { + text-decoration: none; + background-color: #eee +} + +.nav>li.disabled>a { + color: #777 +} + +.nav>li.disabled>a:focus, .nav>li.disabled>a:hover { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent +} + +.nav .open>a, .nav .open>a:focus, .nav .open>a:hover { + background-color: #eee; + border-color: #337ab7 +} + +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5 +} + +.nav>li>a>img { + max-width: none +} + +.nav-tabs { + border-bottom: 1px solid #ddd +} + +.nav-tabs>li { + float: left; + margin-bottom: -1px +} + +.nav-tabs>li>a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0 +} + +.nav-tabs>li>a:hover { + border-color: #eee #eee #ddd +} + +.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover + { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent +} + +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0 +} + +.nav-tabs.nav-justified>li { + float: none +} + +.nav-tabs.nav-justified>li>a { + margin-bottom: 5px; + text-align: center +} + +.nav-tabs.nav-justified>.dropdown .dropdown-menu { + top: auto; + left: auto +} + +@media ( min-width :768px) { + .nav-tabs.nav-justified>li { + display: table-cell; + width: 1% + } + .nav-tabs.nav-justified>li>a { + margin-bottom: 0 + } +} + +.nav-tabs.nav-justified>li>a { + margin-right: 0; + border-radius: 4px +} + +.nav-tabs.nav-justified>.active>a, .nav-tabs.nav-justified>.active>a:focus, + .nav-tabs.nav-justified>.active>a:hover { + border: 1px solid #ddd +} + +@media ( min-width :768px) { + .nav-tabs.nav-justified>li>a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0 + } + .nav-tabs.nav-justified>.active>a, .nav-tabs.nav-justified>.active>a:focus, + .nav-tabs.nav-justified>.active>a:hover { + border-bottom-color: #fff + } +} + +.nav-pills>li { + float: left +} + +.nav-pills>li>a { + border-radius: 4px +} + +.nav-pills>li+li { + margin-left: 2px +} + +.nav-pills>li.active>a, .nav-pills>li.active>a:focus, .nav-pills>li.active>a:hover + { + color: #fff; + background-color: #337ab7 +} + +.nav-stacked>li { + float: none +} + +.nav-stacked>li+li { + margin-top: 2px; + margin-left: 0 +} + +.nav-justified { + width: 100% +} + +.nav-justified>li { + float: none +} + +.nav-justified>li>a { + margin-bottom: 5px; + text-align: center +} + +.nav-justified>.dropdown .dropdown-menu { + top: auto; + left: auto +} + +@media ( min-width :768px) { + .nav-justified>li { + display: table-cell; + width: 1% + } + .nav-justified>li>a { + margin-bottom: 0 + } +} + +.nav-tabs-justified { + border-bottom: 0 +} + +.nav-tabs-justified>li>a { + margin-right: 0; + border-radius: 4px +} + +.nav-tabs-justified>.active>a, .nav-tabs-justified>.active>a:focus, + .nav-tabs-justified>.active>a:hover { + border: 1px solid #ddd +} + +@media ( min-width :768px) { + .nav-tabs-justified>li>a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0 + } + .nav-tabs-justified>.active>a, .nav-tabs-justified>.active>a:focus, + .nav-tabs-justified>.active>a:hover { + border-bottom-color: #fff + } +} + +.tab-content>.tab-pane { + display: none +} + +.tab-content>.active { + display: block +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent +} + +@media ( min-width :768px) { + .navbar { + border-radius: 4px + } +} + +@media ( min-width :768px) { + .navbar-header { + float: left + } +} + +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1) +} + +.navbar-collapse.in { + overflow-y: auto +} + +@media ( min-width :768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important + } + .navbar-collapse.in { + overflow-y: visible + } + .navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse { + padding-right: 0; + padding-left: 0 + } +} + +.navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse + { + max-height: 340px +} + +@media ( max-device-width :480px)and (orientation:landscape) { + .navbar-fixed-bottom .navbar-collapse, .navbar-fixed-top .navbar-collapse + { + max-height: 200px + } +} + +.container-fluid>.navbar-collapse, .container-fluid>.navbar-header, + .container>.navbar-collapse, .container>.navbar-header { + margin-right: -15px; + margin-left: -15px +} + +@media ( min-width :768px) { + .container-fluid>.navbar-collapse, .container-fluid>.navbar-header, + .container>.navbar-collapse, .container>.navbar-header { + margin-right: 0; + margin-left: 0 + } +} + +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px +} + +@media ( min-width :768px) { + .navbar-static-top { + border-radius: 0 + } +} + +.navbar-fixed-bottom, .navbar-fixed-top { + position: fixed; + right: 0; + left: 0; + z-index: 1030 +} + +@media ( min-width :768px) { + .navbar-fixed-bottom, .navbar-fixed-top { + border-radius: 0 + } +} + +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0 +} + +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px +} + +.navbar-brand:focus, .navbar-brand:hover { + text-decoration: none +} + +.navbar-brand>img { + display: block +} + +@media ( min-width :768px) { + .navbar>.container .navbar-brand, .navbar>.container-fluid .navbar-brand + { + margin-left: -15px + } +} + +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px +} + +.navbar-toggle:focus { + outline: 0 +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px +} + +.navbar-toggle .icon-bar+.icon-bar { + margin-top: 4px +} + +@media ( min-width :768px) { + .navbar-toggle { + display: none + } +} + +.navbar-nav { + margin: 7.5px -15px +} + +.navbar-nav>li>a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px +} + +@media ( max-width :767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none + } + .navbar-nav .open .dropdown-menu .dropdown-header, .navbar-nav .open .dropdown-menu>li>a + { + padding: 5px 15px 5px 25px + } + .navbar-nav .open .dropdown-menu>li>a { + line-height: 20px + } + .navbar-nav .open .dropdown-menu>li>a:focus, .navbar-nav .open .dropdown-menu>li>a:hover + { + background-image: none + } +} + +@media ( min-width :768px) { + .navbar-nav { + float: left; + margin: 0 + } + .navbar-nav>li { + float: left + } + .navbar-nav>li>a { + padding-top: 15px; + padding-bottom: 15px + } +} + +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 + rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 + rgba(255, 255, 255, .1) +} + +@media ( min-width :768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + .navbar-form .form-control-static { + display: inline-block + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle + } + .navbar-form .input-group .form-control, .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn { + width: auto + } + .navbar-form .input-group>.form-control { + width: 100% + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle + } + .navbar-form .checkbox, .navbar-form .radio { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle + } + .navbar-form .checkbox label, .navbar-form .radio label { + padding-left: 0 + } + .navbar-form .checkbox input[type=checkbox], .navbar-form .radio input[type=radio] + { + position: relative; + margin-left: 0 + } + .navbar-form .has-feedback .form-control-feedback { + top: 0 + } +} + +@media ( max-width :767px) { + .navbar-form .form-group { + margin-bottom: 5px + } + .navbar-form .form-group:last-child { + margin-bottom: 0 + } +} + +@media ( min-width :768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none + } +} + +.navbar-nav>li>.dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px +} + +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px +} + +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px +} + +.navbar-text { + margin-top: 15px; + margin-bottom: 15px +} + +@media ( min-width :768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px + } +} + +@media ( min-width :768px) { + .navbar-left { + float: left !important + } + .navbar-right { + float: right !important; + margin-right: -15px + } + .navbar-right ~.navbar-right { + margin-right: 0 + } +} + +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7 +} + +.navbar-default .navbar-brand { + color: #777 +} + +.navbar-default .navbar-brand:focus, .navbar-default .navbar-brand:hover + { + color: #5e5e5e; + background-color: transparent +} + +.navbar-default .navbar-text { + color: #777 +} + +.navbar-default .navbar-nav>li>a { + color: #777 +} + +.navbar-default .navbar-nav>li>a:focus, .navbar-default .navbar-nav>li>a:hover + { + color: #333; + background-color: transparent +} + +.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:focus, + .navbar-default .navbar-nav>.active>a:hover { + color: #555; + background-color: #e7e7e7 +} + +.navbar-default .navbar-nav>.disabled>a, .navbar-default .navbar-nav>.disabled>a:focus, + .navbar-default .navbar-nav>.disabled>a:hover { + color: #ccc; + background-color: transparent +} + +.navbar-default .navbar-toggle { + border-color: #ddd +} + +.navbar-default .navbar-toggle:focus, .navbar-default .navbar-toggle:hover + { + background-color: #ddd +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #888 +} + +.navbar-default .navbar-collapse, .navbar-default .navbar-form { + border-color: #e7e7e7 +} + +.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:focus, + .navbar-default .navbar-nav>.open>a:hover { + color: #555; + background-color: #e7e7e7 +} + +@media ( max-width :767px) { + .navbar-default .navbar-nav .open .dropdown-menu>li>a { + color: #777 + } + .navbar-default .navbar-nav .open .dropdown-menu>li>a:focus, + .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover { + color: #333; + background-color: transparent + } + .navbar-default .navbar-nav .open .dropdown-menu>.active>a, + .navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus, + .navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover { + color: #555; + background-color: #e7e7e7 + } + .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a, + .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus, + .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover { + color: #ccc; + background-color: transparent + } +} + +.navbar-default .navbar-link { + color: #777 +} + +.navbar-default .navbar-link:hover { + color: #333 +} + +.navbar-default .btn-link { + color: #777 +} + +.navbar-default .btn-link:focus, .navbar-default .btn-link:hover { + color: #333 +} + +.navbar-default .btn-link[disabled]:focus, .navbar-default .btn-link[disabled]:hover, + fieldset[disabled] .navbar-default .btn-link:focus, fieldset[disabled] .navbar-default .btn-link:hover + { + color: #ccc +} + +.navbar-inverse { + background-color: #222; + border-color: #080808 +} + +.navbar-inverse .navbar-brand { + color: #9d9d9d +} + +.navbar-inverse .navbar-brand:focus, .navbar-inverse .navbar-brand:hover + { + color: #fff; + background-color: transparent +} + +.navbar-inverse .navbar-text { + color: #9d9d9d +} + +.navbar-inverse .navbar-nav>li>a { + color: #9d9d9d +} + +.navbar-inverse .navbar-nav>li>a:focus, .navbar-inverse .navbar-nav>li>a:hover + { + color: #fff; + background-color: transparent +} + +.navbar-inverse .navbar-nav>.active>a, .navbar-inverse .navbar-nav>.active>a:focus, + .navbar-inverse .navbar-nav>.active>a:hover { + color: #fff; + background-color: #080808 +} + +.navbar-inverse .navbar-nav>.disabled>a, .navbar-inverse .navbar-nav>.disabled>a:focus, + .navbar-inverse .navbar-nav>.disabled>a:hover { + color: #444; + background-color: transparent +} + +.navbar-inverse .navbar-toggle { + border-color: #333 +} + +.navbar-inverse .navbar-toggle:focus, .navbar-inverse .navbar-toggle:hover + { + background-color: #333 +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff +} + +.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { + border-color: #101010 +} + +.navbar-inverse .navbar-nav>.open>a, .navbar-inverse .navbar-nav>.open>a:focus, + .navbar-inverse .navbar-nav>.open>a:hover { + color: #fff; + background-color: #080808 +} + +@media ( max-width :767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header { + border-color: #080808 + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808 + } + .navbar-inverse .navbar-nav .open .dropdown-menu>li>a { + color: #9d9d9d + } + .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus, + .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover { + color: #fff; + background-color: transparent + } + .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a, + .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus, + .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover { + color: #fff; + background-color: #080808 + } + .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a, + .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus, + .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover { + color: #444; + background-color: transparent + } +} + +.navbar-inverse .navbar-link { + color: #9d9d9d +} + +.navbar-inverse .navbar-link:hover { + color: #fff +} + +.navbar-inverse .btn-link { + color: #9d9d9d +} + +.navbar-inverse .btn-link:focus, .navbar-inverse .btn-link:hover { + color: #fff +} + +.navbar-inverse .btn-link[disabled]:focus, .navbar-inverse .btn-link[disabled]:hover, + fieldset[disabled] .navbar-inverse .btn-link:focus, fieldset[disabled] .navbar-inverse .btn-link:hover + { + color: #444 +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px +} + +.breadcrumb>li { + display: inline-block +} + +.breadcrumb>li+li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0" +} + +.breadcrumb>.active { + color: #777 +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px +} + +.pagination>li { + display: inline +} + +.pagination>li>a, .pagination>li>span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd +} + +.pagination>li:first-child>a, .pagination>li:first-child>span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px +} + +.pagination>li:last-child>a, .pagination>li:last-child>span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px +} + +.pagination>li>a:focus, .pagination>li>a:hover, .pagination>li>span:focus, + .pagination>li>span:hover { + color: #23527c; + background-color: #eee; + border-color: #ddd +} + +.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover, + .pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover + { + z-index: 2; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7 +} + +.pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, + .pagination>.disabled>span, .pagination>.disabled>span:focus, + .pagination>.disabled>span:hover { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd +} + +.pagination-lg>li>a, .pagination-lg>li>span { + padding: 10px 16px; + font-size: 18px +} + +.pagination-lg>li:first-child>a, .pagination-lg>li:first-child>span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px +} + +.pagination-lg>li:last-child>a, .pagination-lg>li:last-child>span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px +} + +.pagination-sm>li>a, .pagination-sm>li>span { + padding: 5px 10px; + font-size: 12px +} + +.pagination-sm>li:first-child>a, .pagination-sm>li:first-child>span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px +} + +.pagination-sm>li:last-child>a, .pagination-sm>li:last-child>span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px +} + +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none +} + +.pager li { + display: inline +} + +.pager li>a, .pager li>span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px +} + +.pager li>a:focus, .pager li>a:hover { + text-decoration: none; + background-color: #eee +} + +.pager .next>a, .pager .next>span { + float: right +} + +.pager .previous>a, .pager .previous>span { + float: left +} + +.pager .disabled>a, .pager .disabled>a:focus, .pager .disabled>a:hover, + .pager .disabled>span { + color: #777; + cursor: not-allowed; + background-color: #fff +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em +} + +a.label:focus, a.label:hover { + color: #fff; + text-decoration: none; + cursor: pointer +} + +.label:empty { + display: none +} + +.btn .label { + position: relative; + top: -1px +} + +.label-default { + background-color: #777 +} + +.label-default[href]:focus, .label-default[href]:hover { + background-color: #5e5e5e +} + +.label-primary { + background-color: #337ab7 +} + +.label-primary[href]:focus, .label-primary[href]:hover { + background-color: #286090 +} + +.label-success { + background-color: #5cb85c +} + +.label-success[href]:focus, .label-success[href]:hover { + background-color: #449d44 +} + +.label-info { + background-color: #5bc0de +} + +.label-info[href]:focus, .label-info[href]:hover { + background-color: #31b0d5 +} + +.label-warning { + background-color: #f0ad4e +} + +.label-warning[href]:focus, .label-warning[href]:hover { + background-color: #ec971f +} + +.label-danger { + background-color: #d9534f +} + +.label-danger[href]:focus, .label-danger[href]:hover { + background-color: #c9302c +} + +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #777; + border-radius: 10px +} + +.badge:empty { + display: none +} + +.btn .badge { + position: relative; + top: -1px +} + +.btn-group-xs>.btn .badge, .btn-xs .badge { + top: 0; + padding: 1px 5px +} + +a.badge:focus, a.badge:hover { + color: #fff; + text-decoration: none; + cursor: pointer +} + +.list-group-item.active>.badge, .nav-pills>.active>a>.badge { + color: #337ab7; + background-color: #fff +} + +.list-group-item>.badge { + float: right +} + +.list-group-item>.badge+.badge { + margin-right: 5px +} + +.nav-pills>li>a>.badge { + margin-left: 3px +} + +.jumbotron { + padding: 30px 15px; + margin-bottom: 30px; + color: inherit; + background-color: #eee +} + +.jumbotron .h1, .jumbotron h1 { + color: inherit +} + +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200 +} + +.jumbotron>hr { + border-top-color: #d5d5d5 +} + +.container .jumbotron, .container-fluid .jumbotron { + border-radius: 6px +} + +.jumbotron .container { + max-width: 100% +} + +@media screen and (min-width:768px) { + .jumbotron { + padding: 48px 0 + } + .container .jumbotron, .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px + } + .jumbotron .h1, .jumbotron h1 { + font-size: 63px + } +} + +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out +} + +.thumbnail a>img, .thumbnail>img { + margin-right: auto; + margin-left: auto +} + +a.thumbnail.active, a.thumbnail:focus, a.thumbnail:hover { + border-color: #337ab7 +} + +.thumbnail .caption { + padding: 9px; + color: #333 +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px +} + +.alert h4 { + margin-top: 0; + color: inherit +} + +.alert .alert-link { + font-weight: 700 +} + +.alert>p, .alert>ul { + margin-bottom: 0 +} + +.alert>p+p { + margin-top: 5px +} + +.alert-dismissable, .alert-dismissible { + padding-right: 35px +} + +.alert-dismissable .close, .alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit +} + +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6 +} + +.alert-success hr { + border-top-color: #c9e2b3 +} + +.alert-success .alert-link { + color: #2b542c +} + +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1 +} + +.alert-info hr { + border-top-color: #a6e1ec +} + +.alert-info .alert-link { + color: #245269 +} + +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc +} + +.alert-warning hr { + border-top-color: #f7e1b5 +} + +.alert-warning .alert-link { + color: #66512c +} + +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1 +} + +.alert-danger hr { + border-top-color: #e4b9c0 +} + +.alert-danger .alert-link { + color: #843534 +} + +@ +-webkit-keyframes progress-bar-stripes { + from {background-position: 40px 0 +} + +to { + background-position: 0 0 +} + +} +@ +-o-keyframes progress-bar-stripes { + from {background-position: 40px 0 +} + +to { + background-position: 0 0 +} + +} +@ +keyframes progress-bar-stripes { + from {background-position: 40px 0 +} + +to { + background-position: 0 0 +} + +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1) +} + +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease +} + +.progress-bar-striped, .progress-striped .progress-bar { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) + 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px +} + +.progress-bar.active, .progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite +} + +.progress-bar-success { + background-color: #5cb85c +} + +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) + 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.progress-bar-info { + background-color: #5bc0de +} + +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) + 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.progress-bar-warning { + background-color: #f0ad4e +} + +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) + 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.progress-bar-danger { + background-color: #d9534f +} + +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) + 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, + transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, + rgba(255, 255, 255, .15) 75%, transparent 75%, transparent) +} + +.media { + margin-top: 15px +} + +.media:first-child { + margin-top: 0 +} + +.media, .media-body { + overflow: hidden; + zoom: 1 +} + +.media-body { + width: 10000px +} + +.media-object { + display: block +} + +.media-right, .media>.pull-right { + padding-left: 10px +} + +.media-left, .media>.pull-left { + padding-right: 10px +} + +.media-body, .media-left, .media-right { + display: table-cell; + vertical-align: top +} + +.media-middle { + vertical-align: middle +} + +.media-bottom { + vertical-align: bottom +} + +.media-heading { + margin-top: 0; + margin-bottom: 5px +} + +.media-list { + padding-left: 0; + list-style: none +} + +.list-group { + padding-left: 0; + margin-bottom: 20px +} + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd +} + +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px +} + +a.list-group-item { + color: #555 +} + +a.list-group-item .list-group-item-heading { + color: #333 +} + +a.list-group-item:focus, a.list-group-item:hover { + color: #555; + text-decoration: none; + background-color: #f5f5f5 +} + +.list-group-item.disabled, .list-group-item.disabled:focus, + .list-group-item.disabled:hover { + color: #777; + cursor: not-allowed; + background-color: #eee +} + +.list-group-item.disabled .list-group-item-heading, .list-group-item.disabled:focus .list-group-item-heading, + .list-group-item.disabled:hover .list-group-item-heading { + color: inherit +} + +.list-group-item.disabled .list-group-item-text, .list-group-item.disabled:focus .list-group-item-text, + .list-group-item.disabled:hover .list-group-item-text { + color: #777 +} + +.list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover + { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7 +} + +.list-group-item.active .list-group-item-heading, .list-group-item.active .list-group-item-heading>.small, + .list-group-item.active .list-group-item-heading>small, + .list-group-item.active:focus .list-group-item-heading, + .list-group-item.active:focus .list-group-item-heading>.small, + .list-group-item.active:focus .list-group-item-heading>small, + .list-group-item.active:hover .list-group-item-heading, + .list-group-item.active:hover .list-group-item-heading>.small, + .list-group-item.active:hover .list-group-item-heading>small { + color: inherit +} + +.list-group-item.active .list-group-item-text, .list-group-item.active:focus .list-group-item-text, + .list-group-item.active:hover .list-group-item-text { + color: #c7ddef +} + +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8 +} + +a.list-group-item-success { + color: #3c763d +} + +a.list-group-item-success .list-group-item-heading { + color: inherit +} + +a.list-group-item-success:focus, a.list-group-item-success:hover { + color: #3c763d; + background-color: #d0e9c6 +} + +a.list-group-item-success.active, a.list-group-item-success.active:focus, + a.list-group-item-success.active:hover { + color: #fff; + background-color: #3c763d; + border-color: #3c763d +} + +.list-group-item-info { + color: #31708f; + background-color: #d9edf7 +} + +a.list-group-item-info { + color: #31708f +} + +a.list-group-item-info .list-group-item-heading { + color: inherit +} + +a.list-group-item-info:focus, a.list-group-item-info:hover { + color: #31708f; + background-color: #c4e3f3 +} + +a.list-group-item-info.active, a.list-group-item-info.active:focus, a.list-group-item-info.active:hover + { + color: #fff; + background-color: #31708f; + border-color: #31708f +} + +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3 +} + +a.list-group-item-warning { + color: #8a6d3b +} + +a.list-group-item-warning .list-group-item-heading { + color: inherit +} + +a.list-group-item-warning:focus, a.list-group-item-warning:hover { + color: #8a6d3b; + background-color: #faf2cc +} + +a.list-group-item-warning.active, a.list-group-item-warning.active:focus, + a.list-group-item-warning.active:hover { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b +} + +.list-group-item-danger { + color: #a94442; + background-color: #f2dede +} + +a.list-group-item-danger { + color: #a94442 +} + +a.list-group-item-danger .list-group-item-heading { + color: inherit +} + +a.list-group-item-danger:focus, a.list-group-item-danger:hover { + color: #a94442; + background-color: #ebcccc +} + +a.list-group-item-danger.active, a.list-group-item-danger.active:focus, + a.list-group-item-danger.active:hover { + color: #fff; + background-color: #a94442; + border-color: #a94442 +} + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3 +} + +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05) +} + +.panel-body { + padding: 15px +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel-heading>.dropdown .dropdown-toggle { + color: inherit +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit +} + +.panel-title>.small, .panel-title>.small>a, .panel-title>a, .panel-title>small, + .panel-title>small>a { + color: inherit +} + +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel>.list-group, .panel>.panel-collapse>.list-group { + margin-bottom: 0 +} + +.panel>.list-group .list-group-item, .panel>.panel-collapse>.list-group .list-group-item + { + border-width: 1px 0; + border-radius: 0 +} + +.panel>.list-group:first-child .list-group-item:first-child, .panel>.panel-collapse>.list-group:first-child .list-group-item:first-child + { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel>.list-group:last-child .list-group-item:last-child, .panel>.panel-collapse>.list-group:last-child .list-group-item:last-child + { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel-heading+.list-group .list-group-item:first-child { + border-top-width: 0 +} + +.list-group+.panel-footer { + border-top-width: 0 +} + +.panel>.panel-collapse>.table, .panel>.table, .panel>.table-responsive>.table + { + margin-bottom: 0 +} + +.panel>.panel-collapse>.table caption, .panel>.table caption, .panel>.table-responsive>.table caption + { + padding-right: 15px; + padding-left: 15px +} + +.panel>.table-responsive:first-child>.table:first-child, .panel>.table:first-child + { + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child, + .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child, + .panel>.table:first-child>tbody:first-child>tr:first-child, .panel>.table:first-child>thead:first-child>tr:first-child + { + border-top-left-radius: 3px; + border-top-right-radius: 3px +} + +.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child, + .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child, + .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child, + .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child, + .panel>.table:first-child>tbody:first-child>tr:first-child td:first-child, + .panel>.table:first-child>tbody:first-child>tr:first-child th:first-child, + .panel>.table:first-child>thead:first-child>tr:first-child td:first-child, + .panel>.table:first-child>thead:first-child>tr:first-child th:first-child + { + border-top-left-radius: 3px +} + +.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child, + .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child, + .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child, + .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child, + .panel>.table:first-child>tbody:first-child>tr:first-child td:last-child, + .panel>.table:first-child>tbody:first-child>tr:first-child th:last-child, + .panel>.table:first-child>thead:first-child>tr:first-child td:last-child, + .panel>.table:first-child>thead:first-child>tr:first-child th:last-child + { + border-top-right-radius: 3px +} + +.panel>.table-responsive:last-child>.table:last-child, .panel>.table:last-child + { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child, + .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child, + .panel>.table:last-child>tbody:last-child>tr:last-child, .panel>.table:last-child>tfoot:last-child>tr:last-child + { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px +} + +.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child, + .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child, + .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child, + .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child, + .panel>.table:last-child>tbody:last-child>tr:last-child td:first-child, + .panel>.table:last-child>tbody:last-child>tr:last-child th:first-child, + .panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child, + .panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child + { + border-bottom-left-radius: 3px +} + +.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child, + .panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child, + .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child, + .panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child, + .panel>.table:last-child>tbody:last-child>tr:last-child td:last-child, + .panel>.table:last-child>tbody:last-child>tr:last-child th:last-child, + .panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child, + .panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child { + border-bottom-right-radius: 3px +} + +.panel>.panel-body+.table, .panel>.panel-body+.table-responsive, .panel>.table+.panel-body, + .panel>.table-responsive+.panel-body { + border-top: 1px solid #ddd +} + +.panel>.table>tbody:first-child>tr:first-child td, .panel>.table>tbody:first-child>tr:first-child th + { + border-top: 0 +} + +.panel>.table-bordered, .panel>.table-responsive>.table-bordered { + border: 0 +} + +.panel>.table-bordered>tbody>tr>td:first-child, .panel>.table-bordered>tbody>tr>th:first-child, + .panel>.table-bordered>tfoot>tr>td:first-child, .panel>.table-bordered>tfoot>tr>th:first-child, + .panel>.table-bordered>thead>tr>td:first-child, .panel>.table-bordered>thead>tr>th:first-child, + .panel>.table-responsive>.table-bordered>tbody>tr>td:first-child, + .panel>.table-responsive>.table-bordered>tbody>tr>th:first-child, + .panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child, + .panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child, + .panel>.table-responsive>.table-bordered>thead>tr>td:first-child, + .panel>.table-responsive>.table-bordered>thead>tr>th:first-child { + border-left: 0 +} + +.panel>.table-bordered>tbody>tr>td:last-child, .panel>.table-bordered>tbody>tr>th:last-child, + .panel>.table-bordered>tfoot>tr>td:last-child, .panel>.table-bordered>tfoot>tr>th:last-child, + .panel>.table-bordered>thead>tr>td:last-child, .panel>.table-bordered>thead>tr>th:last-child, + .panel>.table-responsive>.table-bordered>tbody>tr>td:last-child, .panel>.table-responsive>.table-bordered>tbody>tr>th:last-child, + .panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child, .panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child, + .panel>.table-responsive>.table-bordered>thead>tr>td:last-child, .panel>.table-responsive>.table-bordered>thead>tr>th:last-child + { + border-right: 0 +} + +.panel>.table-bordered>tbody>tr:first-child>td, .panel>.table-bordered>tbody>tr:first-child>th, + .panel>.table-bordered>thead>tr:first-child>td, .panel>.table-bordered>thead>tr:first-child>th, + .panel>.table-responsive>.table-bordered>tbody>tr:first-child>td, + .panel>.table-responsive>.table-bordered>tbody>tr:first-child>th, + .panel>.table-responsive>.table-bordered>thead>tr:first-child>td, + .panel>.table-responsive>.table-bordered>thead>tr:first-child>th { + border-bottom: 0 +} + +.panel>.table-bordered>tbody>tr:last-child>td, .panel>.table-bordered>tbody>tr:last-child>th, + .panel>.table-bordered>tfoot>tr:last-child>td, .panel>.table-bordered>tfoot>tr:last-child>th, + .panel>.table-responsive>.table-bordered>tbody>tr:last-child>td, .panel>.table-responsive>.table-bordered>tbody>tr:last-child>th, + .panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td, .panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th + { + border-bottom: 0 +} + +.panel>.table-responsive { + margin-bottom: 0; + border: 0 +} + +.panel-group { + margin-bottom: 20px +} + +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px +} + +.panel-group .panel+.panel { + margin-top: 5px +} + +.panel-group .panel-heading { + border-bottom: 0 +} + +.panel-group .panel-heading+.panel-collapse>.list-group, .panel-group .panel-heading+.panel-collapse>.panel-body + { + border-top: 1px solid #ddd +} + +.panel-group .panel-footer { + border-top: 0 +} + +.panel-group .panel-footer+.panel-collapse .panel-body { + border-bottom: 1px solid #ddd +} + +.panel-default { + border-color: #ddd +} + +.panel-default>.panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd +} + +.panel-default>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #ddd +} + +.panel-default>.panel-heading .badge { + color: #f5f5f5; + background-color: #333 +} + +.panel-default>.panel-footer+.panel-collapse>.panel-body { + border-bottom-color: #ddd +} + +.panel-primary { + border-color: #337ab7 +} + +.panel-primary>.panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7 +} + +.panel-primary>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #337ab7 +} + +.panel-primary>.panel-heading .badge { + color: #337ab7; + background-color: #fff +} + +.panel-primary>.panel-footer+.panel-collapse>.panel-body { + border-bottom-color: #337ab7 +} + +.panel-success { + border-color: #d6e9c6 +} + +.panel-success>.panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6 +} + +.panel-success>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #d6e9c6 +} + +.panel-success>.panel-heading .badge { + color: #dff0d8; + background-color: #3c763d +} + +.panel-success>.panel-footer+.panel-collapse>.panel-body { + border-bottom-color: #d6e9c6 +} + +.panel-info { + border-color: #bce8f1 +} + +.panel-info>.panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1 +} + +.panel-info>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #bce8f1 +} + +.panel-info>.panel-heading .badge { + color: #d9edf7; + background-color: #31708f +} + +.panel-info>.panel-footer+.panel-collapse>.panel-body { + border-bottom-color: #bce8f1 +} + +.panel-warning { + border-color: #faebcc +} + +.panel-warning>.panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc +} + +.panel-warning>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #faebcc +} + +.panel-warning>.panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b +} + +.panel-warning>.panel-footer+.panel-collapse>.panel-body { + border-bottom-color: #faebcc +} + +.panel-danger { + border-color: #ebccd1 +} + +.panel-danger>.panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1 +} + +.panel-danger>.panel-heading+.panel-collapse>.panel-body { + border-top-color: #ebccd1 +} + +.panel-danger>.panel-heading .badge { + color: #f2dede; + background-color: #a94442 +} + +.panel-danger>.panel-footer+.panel-collapse>.panel-body { + border-bottom-color: #ebccd1 +} + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden +} + +.embed-responsive .embed-responsive-item, .embed-responsive embed, + .embed-responsive iframe, .embed-responsive object, .embed-responsive video + { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0 +} + +.embed-responsive-16by9 { + padding-bottom: 56.25% +} + +.embed-responsive-4by3 { + padding-bottom: 75% +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05) +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15) +} + +.well-lg { + padding: 24px; + border-radius: 6px +} + +.well-sm { + padding: 9px; + border-radius: 3px +} + +.close { + float: right; + font-size: 21px; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity = 20); + opacity: .2 +} + +.close:focus, .close:hover { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity = 50); + opacity: .5 +} + +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: 0 0; + border: 0 +} + +.modal-open { + overflow: hidden +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0 +} + +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%) +} + +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0) +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto +} + +.modal-dialog { + position: relative; + width: auto; + margin: 10px +} + +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5) +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000 +} + +.modal-backdrop.fade { + filter: alpha(opacity = 0); + opacity: 0 +} + +.modal-backdrop.in { + filter: alpha(opacity = 50); + opacity: .5 +} + +.modal-header { + min-height: 16.43px; + padding: 15px; + border-bottom: 1px solid #e5e5e5 +} + +.modal-header .close { + margin-top: -2px +} + +.modal-title { + margin: 0; + line-height: 1.42857143 +} + +.modal-body { + position: relative; + padding: 15px +} + +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5 +} + +.modal-footer .btn+.btn { + margin-bottom: 0; + margin-left: 5px +} + +.modal-footer .btn-group .btn+.btn { + margin-left: -1px +} + +.modal-footer .btn-block+.btn-block { + margin-left: 0 +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll +} + +@media ( min-width :768px) { + .modal-dialog { + width: 600px; + margin: 30px auto + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5) + } + .modal-sm { + width: 300px + } +} + +@media ( min-width :992px) { + .modal-lg { + width: 900px + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-weight: 400; + line-height: 1.4; + filter: alpha(opacity = 0); + opacity: 0 +} + +.tooltip.in { + filter: alpha(opacity = 90); + opacity: .9 +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000 +} + +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000 +} + +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000 +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000 +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000 +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000 +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000 +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000 +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: 400; + line-height: 1.42857143; + text-align: left; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2) +} + +.popover.top { + margin-top: -10px +} + +.popover.right { + margin-left: 10px +} + +.popover.bottom { + margin-top: 10px +} + +.popover.left { + margin-left: -10px +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0 +} + +.popover-content { + padding: 9px 14px +} + +.popover>.arrow, .popover>.arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid +} + +.popover>.arrow { + border-width: 11px +} + +.popover>.arrow:after { + content: ""; + border-width: 10px +} + +.popover.top>.arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0 +} + +.popover.top>.arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0 +} + +.popover.right>.arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0 +} + +.popover.right>.arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0 +} + +.popover.bottom>.arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25) +} + +.popover.bottom>.arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff +} + +.popover.left>.arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25) +} + +.popover.left>.arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff +} + +.carousel { + position: relative +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden +} + +.carousel-inner>.item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left +} + +.carousel-inner>.item>a>img, .carousel-inner>.item>img { + line-height: 1 +} + +@media all and (transform-3d) , ( -webkit-transform-3d ) { + .carousel-inner>.item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000; + perspective: 1000 + } + .carousel-inner>.item.active.right, .carousel-inner>.item.next { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) + } + .carousel-inner>.item.active.left, .carousel-inner>.item.prev { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) + } + .carousel-inner>.item.active, .carousel-inner>.item.next.left, + .carousel-inner>.item.prev.right { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0) + } +} + +.carousel-inner>.active, .carousel-inner>.next, .carousel-inner>.prev { + display: block +} + +.carousel-inner>.active { + left: 0 +} + +.carousel-inner>.next, .carousel-inner>.prev { + position: absolute; + top: 0; + width: 100% +} + +.carousel-inner>.next { + left: 100% +} + +.carousel-inner>.prev { + left: -100% +} + +.carousel-inner>.next.left, .carousel-inner>.prev.right { + left: 0 +} + +.carousel-inner>.active.left { + left: -100% +} + +.carousel-inner>.active.right { + left: 100% +} + +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity = 50); + opacity: .5 +} + +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0, + rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0, + rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), + to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0, + rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', + endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x +} + +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0, + rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0, + rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), + to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0, + rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', + endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x +} + +.carousel-control:focus, .carousel-control:hover { + color: #fff; + text-decoration: none; + filter: alpha(opacity = 90); + outline: 0; + opacity: .9 +} + +.carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next, .carousel-control .icon-prev { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block +} + +.carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev + { + left: 50%; + margin-left: -10px +} + +.carousel-control .glyphicon-chevron-right, .carousel-control .icon-next + { + right: 50%; + margin-right: -10px +} + +.carousel-control .icon-next, .carousel-control .icon-prev { + width: 20px; + height: 20px; + margin-top: -10px; + font-family: serif; + line-height: 1 +} + +.carousel-control .icon-prev:before { + content: '\2039' +} + +.carousel-control .icon-next:before { + content: '\203a' +} + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none +} + +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px +} + +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6) +} + +.carousel-caption .btn { + text-shadow: none +} + +@media screen and (min-width:768px) { + .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next, .carousel-control .icon-prev { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px + } + .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev + { + margin-left: -15px + } + .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next + { + margin-right: -15px + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px + } + .carousel-indicators { + bottom: 20px + } +} + +.btn-group-vertical>.btn-group:after, .btn-group-vertical>.btn-group:before, + .btn-toolbar:after, .btn-toolbar:before, .clearfix:after, .clearfix:before, + .container-fluid:after, .container-fluid:before, .container:after, + .container:before, .dl-horizontal dd:after, .dl-horizontal dd:before, + .form-horizontal .form-group:after, .form-horizontal .form-group:before, + .modal-footer:after, .modal-footer:before, .nav:after, .nav:before, + .navbar-collapse:after, .navbar-collapse:before, .navbar-header:after, + .navbar-header:before, .navbar:after, .navbar:before, .pager:after, + .pager:before, .panel-body:after, .panel-body:before, .row:after, .row:before + { + display: table; + content: " " +} + +.btn-group-vertical>.btn-group:after, .btn-toolbar:after, .clearfix:after, + .container-fluid:after, .container:after, .dl-horizontal dd:after, + .form-horizontal .form-group:after, .modal-footer:after, .nav:after, + .navbar-collapse:after, .navbar-header:after, .navbar:after, .pager:after, + .panel-body:after, .row:after { + clear: both +} + +.center-block { + display: block; + margin-right: auto; + margin-left: auto +} + +.pull-right { + float: right !important +} + +.pull-left { + float: left !important +} + +.hide { + display: none !important +} + +.show { + display: block !important +} + +.invisible { + visibility: hidden +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0 +} + +.hidden { + display: none !important +} + +.affix { + position: fixed +} + +@ +-ms-viewport { + width: device-width +} + +.visible-lg, .visible-md, .visible-sm, .visible-xs { + display: none !important +} + +.visible-lg-block, .visible-lg-inline, .visible-lg-inline-block, + .visible-md-block, .visible-md-inline, .visible-md-inline-block, + .visible-sm-block, .visible-sm-inline, .visible-sm-inline-block, + .visible-xs-block, .visible-xs-inline, .visible-xs-inline-block { + display: none !important +} + +@media ( max-width :767px) { + .visible-xs { + display: block !important + } + table.visible-xs { + display: table + } + tr.visible-xs { + display: table-row !important + } + td.visible-xs, th.visible-xs { + display: table-cell !important + } +} + +@media ( max-width :767px) { + .visible-xs-block { + display: block !important + } +} + +@media ( max-width :767px) { + .visible-xs-inline { + display: inline !important + } +} + +@media ( max-width :767px) { + .visible-xs-inline-block { + display: inline-block !important + } +} + +@media ( min-width :768px)and (max-width:991px) { + .visible-sm { + display: block !important + } + table.visible-sm { + display: table + } + tr.visible-sm { + display: table-row !important + } + td.visible-sm, th.visible-sm { + display: table-cell !important + } +} + +@media ( min-width :768px)and (max-width:991px) { + .visible-sm-block { + display: block !important + } +} + +@media ( min-width :768px)and (max-width:991px) { + .visible-sm-inline { + display: inline !important + } +} + +@media ( min-width :768px)and (max-width:991px) { + .visible-sm-inline-block { + display: inline-block !important + } +} + +@media ( min-width :992px)and (max-width:1199px) { + .visible-md { + display: block !important + } + table.visible-md { + display: table + } + tr.visible-md { + display: table-row !important + } + td.visible-md, th.visible-md { + display: table-cell !important + } +} + +@media ( min-width :992px)and (max-width:1199px) { + .visible-md-block { + display: block !important + } +} + +@media ( min-width :992px)and (max-width:1199px) { + .visible-md-inline { + display: inline !important + } +} + +@media ( min-width :992px)and (max-width:1199px) { + .visible-md-inline-block { + display: inline-block !important + } +} + +@media ( min-width :1200px) { + .visible-lg { + display: block !important + } + table.visible-lg { + display: table + } + tr.visible-lg { + display: table-row !important + } + td.visible-lg, th.visible-lg { + display: table-cell !important + } +} + +@media ( min-width :1200px) { + .visible-lg-block { + display: block !important + } +} + +@media ( min-width :1200px) { + .visible-lg-inline { + display: inline !important + } +} + +@media ( min-width :1200px) { + .visible-lg-inline-block { + display: inline-block !important + } +} + +@media ( max-width :767px) { + .hidden-xs { + display: none !important + } +} + +@media ( min-width :768px)and (max-width:991px) { + .hidden-sm { + display: none !important + } +} + +@media ( min-width :992px)and (max-width:1199px) { + .hidden-md { + display: none !important + } +} + +@media ( min-width :1200px) { + .hidden-lg { + display: none !important + } +} + +.visible-print { + display: none !important +} + +@media print { + .visible-print { + display: block !important + } + table.visible-print { + display: table + } + tr.visible-print { + display: table-row !important + } + td.visible-print, th.visible-print { + display: table-cell !important + } +} + +.visible-print-block { + display: none !important +} + +@media print { + .visible-print-block { + display: block !important + } +} + +.visible-print-inline { + display: none !important +} + +@media print { + .visible-print-inline { + display: inline !important + } +} + +.visible-print-inline-block { + display: none !important +} + +@media print { + .visible-print-inline-block { + display: inline-block !important + } +} + +@media print { + .hidden-print { + display: none !important + } +} \ No newline at end of file diff --git a/_examples/intermediate/serve-embedded-files/assets/favicon.ico b/_examples/intermediate/serve-embedded-files/assets/favicon.ico new file mode 100644 index 00000000..de3d65eb Binary files /dev/null and b/_examples/intermediate/serve-embedded-files/assets/favicon.ico differ diff --git a/_examples/intermediate/serve-embedded-files/assets/js/jquery-2.1.1.js b/_examples/intermediate/serve-embedded-files/assets/js/jquery-2.1.1.js new file mode 100644 index 00000000..9f7b3d38 --- /dev/null +++ b/_examples/intermediate/serve-embedded-files/assets/js/jquery-2.1.1.js @@ -0,0 +1,9190 @@ +/*! + * jQuery JavaScript Library v2.1.1 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-05-01T17:11Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// + +var arr = []; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + version = "2.1.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android < 4.0, iOS < 6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter(function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android < 4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Math.random(); +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android < 4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ((key && typeof key === "string") && value === undefined) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(key) ); + } + + // [*]When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = new Data(); + + + +/* + Implementation Summary + + 1. Enforce API surface and semantic compatibility with 1.9.x branch + 2. Improve the module's maintainability by reducing the storage + paths to a single mechanism. + 3. Use the same single mechanism to support "private" and "user" data. + 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) + 5. Avoid exposing implementation details on user objects (eg. expando properties) + 6. Provide a clear path for implementation upgrade to WeakMap in 2014 +*/ +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.remove( elem, name ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + // Attempt to get data from the cache + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.access( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.remove( elem, [ type + "queue", key ] ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = data_priv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + // Support: Windows Web Apps (WWA) + // `name` and `type` need .setAttribute for WWA + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE9-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.hasData( elem ) && data_priv.get( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome < 28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// Create "bubbling" focus and blur events +// Support: Firefox, Chrome, Safari +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); + + +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE 9 + option: [ 1, "" ], + + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] + }; + +// Support: IE 9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } + + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// Support: IE >= 9 +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Support: IE >= 9 + // Fix Cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Fixes #12346 + // Support: Webkit, IE + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "