././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.434259 heat_dashboard-16.0.0/0000775000175000017500000000000015257164606013364 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4754963 heat_dashboard-16.0.0/.coveragerc0000664000175000017500000000015215257164522015500 0ustar00zuulzuul[run] branch = True source = heat_dashboard omit = heat_dashboard/tests/* [report] ignore_errors = True ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4764962 heat_dashboard-16.0.0/.eslintignore0000664000175000017500000000012415257164522016061 0ustar00zuulzuulheat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/libs/* ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4764962 heat_dashboard-16.0.0/.eslintrc.js0000664000175000017500000002036215257164522015623 0ustar00zuulzuulmodule.exports = { "env": { "browser": true, "jasmine": true, }, "plugins": ["jasmine"], "globals": { "angular": true, "module": true, "inject": true, "saveAs": true, "json2yaml": true }, "extends": ["eslint:recommended", "plugin:jasmine/recommended"], "rules": { "accessor-pairs": "error", "array-bracket-newline": "off", "array-bracket-spacing": "off", "array-callback-return": "error", "array-element-newline": "off", "arrow-body-style": "error", "arrow-parens": "error", "arrow-spacing": "error", "block-scoped-var": "off", "block-spacing": "off", "brace-style": "off", "callback-return": "off", "camelcase": "off", "capitalized-comments": "off", "class-methods-use-this": "error", "comma-dangle": "off", "comma-spacing": "off", "comma-style": "off", "complexity": "off", "computed-property-spacing": [ "error", "never" ], "consistent-return": "off", "consistent-this": "off", "curly": "off", "default-case": "off", "dot-location": [ "error", "property" ], "dot-notation": "off", "eol-last": "off", "eqeqeq": "off", "for-direction": "error", "func-call-spacing": "off", "func-name-matching": "off", "func-names": "off", "func-style": "off", "function-paren-newline": "off", "generator-star-spacing": "error", "getter-return": "error", "global-require": "error", "guard-for-in": "off", "handle-callback-err": "error", "id-blacklist": "error", "id-length": "off", "id-match": "error", "indent": "off", "indent-legacy": "off", "init-declarations": "off", "jasmine/prefer-toHaveBeenCalledWith": "off", "jsx-quotes": "error", "key-spacing": "off", "keyword-spacing": "off", "line-comment-position": "off", "linebreak-style": [ "error", "unix" ], "lines-around-comment": "off", "lines-around-directive": "off", "lines-between-class-members": "error", "max-depth": "off", "max-len": "off", "max-lines": "off", "max-nested-callbacks": "error", "max-params": "off", "max-statements": "off", "max-statements-per-line": "off", "multiline-comment-style": "off", "multiline-ternary": "off", "new-parens": "off", "newline-after-var": "off", "newline-before-return": "off", "newline-per-chained-call": "off", "no-alert": "off", "no-array-constructor": "error", "no-await-in-loop": "error", "no-bitwise": "off", "no-buffer-constructor": "error", "no-caller": "error", "no-catch-shadow": "off", "no-confusing-arrow": "error", "no-continue": "off", "no-div-regex": "error", "no-duplicate-imports": "error", "no-else-return": "off", "no-empty-function": "off", "no-eq-null": "off", "no-eval": "error", "no-extend-native": "error", "no-extra-bind": "off", "no-extra-label": "off", "no-extra-parens": "off", "no-floating-decimal": "off", "no-implicit-coercion": [ "error", { "boolean": false, "number": false, "string": false } ], "no-implicit-globals": "off", "no-implied-eval": "error", "no-inline-comments": "off", "no-inner-declarations": [ "error", "functions" ], "no-invalid-this": "off", "no-iterator": "error", "no-label-var": "error", "no-lone-blocks": "off", "no-lonely-if": "off", "no-loop-func": "off", "no-magic-numbers": "off", "no-mixed-operators": "off", "no-mixed-requires": "error", "no-multi-assign": "off", "no-multi-spaces": "off", "no-multi-str": "error", "no-multiple-empty-lines": "off", "no-native-reassign": "error", "no-negated-condition": "off", "no-negated-in-lhs": "error", "no-nested-ternary": "off", "no-new": "error", "no-new-func": "off", "no-new-object": "error", "no-new-require": "error", "no-new-wrappers": "error", "no-octal-escape": "error", "no-param-reassign": "off", "no-path-concat": "error", "no-plusplus": "off", "no-process-env": "error", "no-process-exit": "error", "no-proto": "off", "no-prototype-builtins": "off", "no-redeclare": "off", "no-restricted-globals": "error", "no-restricted-imports": "error", "no-restricted-modules": "error", "no-restricted-properties": "error", "no-restricted-syntax": "error", "no-return-assign": "off", "no-return-await": "error", "no-script-url": "error", "no-self-compare": "error", "no-sequences": "off", "no-shadow": "off", "no-shadow-restricted-names": "off", "no-spaced-func": "off", "no-sync": "error", "no-tabs": "off", "no-template-curly-in-string": "error", "no-ternary": "off", "no-throw-literal": "off", "no-trailing-spaces": [ "error", { "ignoreComments": true, "skipBlankLines": true } ], "no-undef-init": "off", "no-undefined": "off", "no-underscore-dangle": "off", "no-unmodified-loop-condition": "off", "no-unneeded-ternary": "off", "no-unused-expressions": "off", "no-unused-vars": "off", "no-use-before-define": "off", "no-useless-call": "off", "no-useless-computed-key": "error", "no-useless-concat": "off", "no-useless-constructor": "error", "no-useless-rename": "error", "no-useless-return": "off", "no-var": "off", "no-void": "off", "no-warning-comments": "off", "no-whitespace-before-property": "error", "no-with": "error", "nonblock-statement-body-position": [ "error", "any" ], "object-curly-newline": "off", "object-curly-spacing": "off", "object-property-newline": "off", "object-shorthand": "off", "one-var": "off", "one-var-declaration-per-line": "off", "operator-assignment": "off", "operator-linebreak": "off", "padded-blocks": "off", "padding-line-between-statements": "error", "prefer-arrow-callback": "off", "prefer-const": "error", "prefer-destructuring": "off", "prefer-numeric-literals": "error", "prefer-promise-reject-errors": "error", "prefer-reflect": "off", "prefer-rest-params": "off", "prefer-spread": "off", "prefer-template": "off", "quote-props": "off", "quotes": "off", "radix": "off", "require-await": "error", "require-jsdoc": "off", "rest-spread-spacing": "error", "semi": "off", "semi-spacing": "off", "semi-style": "off", "sort-imports": "error", "sort-keys": "off", "sort-vars": "off", "space-before-blocks": "off", "space-before-function-paren": "off", "space-in-parens": "off", "space-infix-ops": "off", "space-unary-ops": "off", "spaced-comment": "off", "strict": "off", "switch-colon-spacing": "off", "symbol-description": "error", "template-curly-spacing": "error", "template-tag-spacing": "error", "unicode-bom": [ "error", "never" ], "valid-jsdoc": "off", "valid-typeof": [ "error", { "requireStringLiterals": false } ], "vars-on-top": "off", "wrap-iife": "off", "wrap-regex": "off", "yield-star-spacing": "error", "yoda": "off" } }; ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6604953 heat_dashboard-16.0.0/.pre-commit-config.yaml0000664000175000017500000000202415257164523017641 0ustar00zuulzuulrepos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: trailing-whitespace # Replaces or checks mixed line ending - id: mixed-line-ending args: ['--fix', 'lf'] exclude: '.*\.(svg)$' # Forbid files which have a UTF-8 byte-order marker - id: fix-byte-order-marker # Checks that non-binary executables have a proper shebang - id: check-executables-have-shebangs # Check for files that contain merge conflict strings. - id: check-merge-conflict # Check for debugger imports and py37+ breakpoint() # calls in python source - id: debug-statements - id: check-yaml files: .*\.(yaml|yml)$ - repo: https://opendev.org/openstack/hacking rev: 8.1.0 hooks: - id: hacking additional_dependencies: - flake8-import-order>0.19.0,<0.20.0 - horizon - repo: https://github.com/asottile/pyupgrade rev: v3.21.2 hooks: - id: pyupgrade args: [--py3-only] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4774961 heat_dashboard-16.0.0/.zuul.yaml0000664000175000017500000000203415257164522015321 0ustar00zuulzuul- job: name: heat-dashboard-integration-tests parent: horizon-integration-tests required-projects: - name: openstack/horizon - name: openstack/heat - name: openstack/python-heatclient - name: openstack/heat-dashboard roles: - zuul: openstack-infra/devstack - zuul: openstack/horizon irrelevant-files: - ^\.gitignore$ - ^\.gitreview$ - ^\.pre-commit-config\.yaml$ - ^doc/.*$ - ^.*\.rst$ - ^releasenotes/.*$ vars: devstack_plugins: heat: https://opendev.org/openstack/heat heat-dashboard: https://opendev.org/openstack/heat-dashboard devstack_services: horizon: true tox_envlist: integration - project: templates: - check-requirements - horizon-non-primary-django-jobs - horizon-nodejs-jobs - openstack-python3-jobs-horizon - publish-openstack-docs-pti - release-notes-jobs-python3 check: jobs: - heat-dashboard-integration-tests: voting: false ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.1072664 heat_dashboard-16.0.0/AUTHORS0000664000175000017500000001657215257164606014447 0ustar00zuulzuulAaron Sahlin Abishek Subramanian Akihiro MOTOKI Akihiro Motoki Akihiro Motoki Alex Gaynor Ana Krivokapic Ana Malagon Andreas Jaeger Andrew Lazarev Antoine Abélard Ashish Chandra AvnishPal Beth Elwell Bo Wang Brianna Poulos Béla Vancsics Cady_Chen Chad Roberts Charles Short ChenZheng Christian Berendt Chuck Short Chuck Short Cindy Lu Cody A.W. Somerville Corey Bryant CristianFiorentino David Hill David Lyle David Lyle David Lyle Diana Clarke Diana Whitten Dirk Mueller Doug Fish Doug Hellmann Eddie Ramirez Eyal Felipe Reyes Fengqian Gao Gabriel Hurley Gary W. Smith George Peristerakis Ghanshyam Mann Gloria Gu Gábor Antal He Yongli Itxaka Ivan Anfimov Ivan Kolodyazhny Jack Choy James E. Blair James E. Blair Janet Yu Jason E. Rist Jasper Capel Jean-Philippe Evrard Jeremy Stanley JiaHao Li Jianing YANG Jiri Tomasek JiyeYu Joe Gordon Jordan OMara Julie Pichon Julien Danjou Justin Pomeroy KC Wang KC Wang Kahou Lei Kanagaraj Manickam Kazunori Shinohara Keigo Noha Keiichi Hikita Kenji Ishii Kevin Fox Kieran Spear Kun Huang Kun Huang LIU Yulong LIU-Yulong Ladislav Smola Lajos Katona Laura Frank Leandro I. Costantino Lin Hua Cheng LiuNanke Lucas Palm Mark McClain Masco Kaliyamoorthy Matt Borland Matt Borland Matt Riedemann Matthias Runge Maxim Nestratov Maxime Vidori Michael Dovgal Michael Krotscheck Michal Dulko Miguel Grinberg Mike Hagedorn Monty Taylor Nathan Reller Omri Gazitt Ondřej Nový OpenStack Release Bot Paul Belanger Paul Karikh Pavlo Shchelokovskyy Peter Belanyi Pierre Riteau Radomir Dopieralski Rajat Vig Ramaraja Revon Mathews Richard Jones Rob Cresswell Rob Raymond Robert Myers Roman Rader Russell Sim Sam Betts Samantha Blanco Saro Chandra Bhooshan Sascha Peilicke Sascha Peilicke Sean Dague Sean McGinnis Sergei Chipiga Sergey Lukjanov Shane Wang Steve Baker Steve Leon Steve McLellan Sushil Kumar Takashi Kajinami Takashi Kajinami Tatiana Mazur Tatiana Ovchinnikova Thai Tran Thomas Bechtold Thomas Goirand Tim Hosking Tim Schnell Timur Sufiev Travis Tripp Trinh Nguyen Tyr Johanson Vadym Markov Vic Howard Victor Stinner Vlad Okhrimenko Vu Cong Tuan Wu Wenxiang Xinni Ge Ying Zuo YuehuiLei Yves-Gwenael Bourhis Zhenguo Niu ZhijunWei absubram chen.qiaomin@99cloud.net chenxing dixiaoli eric huang.zhiping irisayame jacky06 jing.liuqing jlopezgu jolie kaz_shinohara kenji-ishii lawrancejing lin-hua-cheng lin-hua-cheng lingyongxu liyingjun lvxianguo manchandavishal mareklycka nikunj2512 niuke niuke pengyuesheng ricolin ricolin root shizhihui simon tianliang wangbo wangqi wei.ying wu.chunyang wu.shiming xurong00037997 yaraat zhang.lei zhangboye zhaozhilong zhu.rong zhurong ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4774961 heat_dashboard-16.0.0/CONTRIBUTING.rst0000664000175000017500000000122415257164522016021 0ustar00zuulzuulThe source repository for this project can be found at: https://opendev.org/openstack/heat-dashboard Pull requests submitted through GitHub are not monitored. To start contributing to OpenStack, follow the steps in the contribution guide to set up and use Gerrit: https://docs.openstack.org/contributors/code-and-documentation/quick-start.html Bugs should be filed on Storyboard,: https://storyboard.openstack.org/#!/project/openstack/heat-dashboard For more specific information about contributing to this repository, see the heat-dashboard contributor guide: https://docs.openstack.org/heat-dashboard/latest/contributor/contributing.html ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.000269 heat_dashboard-16.0.0/ChangeLog0000664000175000017500000007766115257164606015157 0ustar00zuulzuulCHANGES ======= 16.0.0 ------ * Fix Python 3 AttributeError masking in Template Generator * Update TOX\_CONSTRAINTS\_FILE for stable/2026.2 * Update .gitreview for stable/2026.2 16.0.0.0rc1 ----------- * Do not install code to build docs * Imported Translations from Zanata * Remove test data for neutronclient * Remove neutronclient leftovers * Imported Translations from Zanata * pre-commit: Bump versions * Remove Python 3.10 support * Drop workaround for old devstack * Update Babel configuration * Imported Translations from Zanata * tox: Use new constraints option * Update master for stable/2026.1 15.0.0 ------ * Drop redundant explicit addition of scss files * Remove unused sphinxcontrib-httpdomain * pre-commit: Bump versions * Skip integration tests for .gitignore update * Bump flake8-import-order * Remove MANIFEST.in * Imported Translations from Zanata * reno: Update master for unmaintained/2024.1 * Skip integration tests for update without functional impact * Run pyupgrade to clean up Python 2 syntaxes * Imported Translations from Zanata * Imported Translations from Zanata * Update master for stable/2025.2 14.0.0 ------ * Adopt new usage of assertFormError * Imported Translations from Zanata * Fix small mistake in text error * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Replace deprecated django.utils.datetime\_safe * Imported Translations from Zanata * Remove url tags from README * Use yaml format policy file * Use systemctl command to manage service * Imported Translations from Zanata * Fix a typo in release note * Remove Python 3.9 support * Revert "Replace license classifier" * Revert "Replace license classifier" * Replace license classifier * Bump minimum pbr * Drop environments for nose * Drop logic for django < 1.9 * add pyproject.toml to support pip 23.1 * tox: Remove basepython * Use pre-commit to run pep8 check * Drop redundant injection of VIRTUAL\_ENV * Update master for stable/2025.1 13.0.0 ------ * reno: Update master for unmaintained/2023.1 * Add note about requirements lower bounds * Drop unnecessary 'x' bit from doc config file * Declare Python 3.12 support * Update master for stable/2024.2 12.0.0 ------ * Bump minimum horizon version * Adopt to network/subnet management by openstacksdk * Drop old excludes * Remove Python 3.8 support * reno: Update master for unmaintained/zed * Imported Translations from Zanata * Update master for stable/2024.1 11.0.0 ------ * reno: Update master for unmaintained/xena * reno: Update master for unmaintained/wallaby * reno: Update master for unmaintained/victoria * Fix template generated by Template Generator * Display coverage report * reno: Update master for unmaintained/yoga * tox: Drop envdir * Bump hacking * Mark python 3.11 supported * Regenerate policy files * Update master for stable/2023.2 10.0.0.0rc1 ----------- * Use py3 as the default runtime for tox * Remove note about old pip behavior * Fix 500 error when fetch data about deleted stack * Imported Translations from Zanata * Add Python 3.10 to supported runtime * Update master for stable/2023.1 9.0.0 ----- * Update \`\`bindep.txt\`\` to sync with the horizon * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Switch to 2023.1 Python3 unit tests and generic template name * Update master for stable/zed 8.0.0 ----- * Imported Translations from Zanata * remove unicode prefix from code * Imported Translations from Zanata * Django 4.x: urlunquote() is removed in 4.0 * Imported Translations from Zanata * Update python testing as per zed cycle testing runtime * Add Python 3.9 to supported runtumes * Use Horizon project template for nodejs jobs * Replace deprecated django.conf.urls.url() * Replace deprecated ugettext\_lazy and ungettext\_lazy * Drop lower-constraints.txt and its testing * Fixed Unit test for Heat-dashboard * Update master for stable/yoga * test: Allow to run a specific test 7.0.0 ----- * Fix UT failure due to horizon system-scope support * Deal with bytestring serialized json * Imported Translations from Zanata * Fix lower-constraints job * Fixed getting the location header * Add Python3 yoga unit tests * Update master for stable/xena 6.0.0 ----- * Use volume v3 API instead of v2 API * Changed minversion in tox to 3.18.0 * [ussuri][goal] Update contributor documentation * Drop horizon-nodejs10-jobs template * setup.cfg: Replace dashes with underscores * Add horizon-nodejs14-job template * Set False to include\_empty\_option * Add Python3 xena unit tests * Update master for stable/wallaby 5.0.0 ----- * Support policy-in-code and deprecated policy 4.1.0 ----- * Imported Translations from Zanata * Run npm nodejs job with Firefox browser * Imported Translations from Zanata * Add Python3 wallaby unit tests * Update master for stable/victoria * Move image\_field\_data method in-tree 4.0.0 ----- * [goal] Migrate testing to ubuntu focal * Imported Translations from Zanata * Switch to newer openstackdocstheme and reno versions * Fix hacking min version to 3.0.1 * Imported Translations from Zanata * Add py38 package metadata * Imported Translations from Zanata * Imported Translations from Zanata * Add Python3 victoria unit tests * Update master for stable/ussuri * Cleanup py27 support * Update hacking for Python3 * Use unittest.mock instead of third party mock 3.0.0 ----- * Drop Django 1.11 support * Revert accidental tox.ini change * s/assertItemsEqual/assertCountEqual/g * Remove six usage (2/2) * Remove six usage (1/2) * translation: drop babel extractor definitions * Imported Translations from Zanata * [ussuri][goal] Drop python 2.7 support and testing * Imported Translations from Zanata * Use Horizon project template for django jobs * Generate PDF documentation * Switch to official Ussuri jobs * Imported Translations from Zanata * Update master for stable/train 2.0.0 ----- * Imported Translations from Zanata * Enforces unicode decoding of data * Changes the data field used to populate the E.N. select * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Drop nodejs4 job * Imported Translations from Zanata * Update the constraints url * Cap sphinx for py2 to match global requirements * Drop use of git.openstack.org * Imported Translations from Zanata * OpenDev Migration Patch * Update master for stable/stein * Switch python3 versions of test jobs to match Train PTI * Update hacking version * Imported Translations from Zanata 1.5.0 ----- * Imported Translations from Zanata * Add python 3.6 unit test job * Add heat-dashboard integration tests * Fix gate failures by a new pycodestyle * fix tox python3 overrides * Use template for lower-constraints * fix bug link in readme * Change openstack-dev to openstack-discuss * Imported Translations from Zanata * switch documentation job to new PTI * import zuul job settings from project-config * Remove mox3 completely * Imported Translations from Zanata * Update reno for stable/rocky 1.4.0 ----- * Drop nose dependencies * Imported Translations from Zanata * Use assertRegex instead of assertRegexpMatches 1.3.0 ----- * Imported Translations from Zanata * Replace embedded static files with XStatic-\* * Add release note link in README * TemplateGenerator: Add Designate::Recordset support * Add py36 testenv * Group resource icons by project * Add support of AutoScalingGroup * Add support of ScalingPolicy * Add support of event list pagination * Template Generator: Add support for OS::Designate::Zone * Imported Translations from Zanata * Update all links in documents * Imported Translations from Zanata * Imported Translations from Zanata * Add python3 django 1.11 job instead of django 2.0 job * Imported Translations from Zanata * Rename test files to test\_\* pattern * Drop mox from HeatRestTestCase * Remove mox3 from TemplateGeneratorTests * Remove mox3 from StackTests * Drop mox3 from ResourceTypesTests * Add support for OS::Swift::Container * Django 2.0 support 1.2.0 ----- * Drop mox3 from TemplateVersionsTests * Require the (soon-to-be) Rocky version of Horizon * Update docs bug link to storyboard * Imported Translations from Zanata * Modified typo on Change Stack's pop-up Window * Imported Translations from Zanata * support py3 * Imported Translations from Zanata * Imported Translations from Zanata 1.1.0 ----- * Imported Translations from Zanata * Improve installation guide * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * add lower-constraints job * Imported Translations from Zanata * Imported Translations from Zanata * Updated from global requirements * Avoid tox-install.sh * Add documentation link to README * Imported Translations from Zanata * Imported Translations from Zanata * Updated from global requirements * Imported Translations from Zanata * Add release note for updated enabled file in rocky * Updated from global requirements * Updated from global requirements * Remove 404 image files * Remove minified js * Fix file path typo for hotgen-main.scss * Fix unix rights of svg files * Delete settings import STATIC\_URL * Imported Translations from Zanata * Rename API\_TIMEOUT and API\_PARALLEL vars * Imported Translations from Zanata * Enables warning-is-error in sphinx * Improve doc structure * Declare heat-dashboard depends on mox * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Update reno for stable/queens * Revert "Revert "Drop django <= 1.10 support"" 1.0.2 ----- * Imported Translations from Zanata * Revert "Drop django <= 1.10 support" * Imported Translations from Zanata * Drop django <= 1.10 support * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Delete hard-coding lines of locale * Imported Translations from Zanata * i18n: Annotate template generator django template properly * Correct django template pattern in babel-django.cfg * Correct security group pull down option * Imported Translations from Zanata * fix error url * Imported Translations from Zanata * Zuul: Remove project name 1.0.1 ----- * Change location of conf files in README * Fix file path typo in devstack/plugin.sh * Ensure json files are provided in the package * Imported Translations from Zanata * Optimise how to retrieve pull-down options * Include policy in tree for ease of deployment * Imported Translations from Zanata * Updated from global requirements * Modify select/change template modal description * Updated from global requirements * Create doc/requirements.txt * Imported Translations from Zanata * Imported Translations from Zanata * Imported Translations from Zanata * Update the home page 1.0.0 ----- * fix typo in releasenote * Added leftover change in horizon * comment out logging configuration for heatclient * Update plugin.sh to resolve a couple of issues * Fix nova server networks config options * Add user doc of template generator * Deleted locale dir & Updated babel-django.cfg * Split out heat own items from Horizon settings * Migrate Heat relevant docs to Heat Dashboard * Remove unnecessary \`$locationProvider\` settings * Change rewriteLinks to false * Align tox\_install.sh with other projects * Fix mistake in devstack installation procedure * Partially Revert "Updated release note toward Queens release." * Updated release note toward Queens release * Fix installation procedure for heat\_policy.json according to Horizon team's advice * Change page header name to Template Generator * Submit generated template file directly * Added procedure to enable heat\_policy.json * Remove test.py * Fix installation procedure due to lack of process * Remove setting of version/release from releasenotes * Updated from global requirements * Taken over policy file for heat from Horizon repository * In future, Horizon team is planning to remove heat relevant logic like, - Heat GUI part - API wrapper client - API rest endpoint creator (like api/rest/heat) * Updated from global requirements * Add resources svg to heat-dashboard * Consume horizon in-tree django variant jobs * Modify Neutron Router modal * Clean up npm package dependencies * Add in repo zuul job definition * Add initial files as devstack settings/documents/requirements * Add unittests for template generator angular * add docs * add release notes * fix tox.ini * add initial files * update files by working files * add template generator * move files * move all files under heat\_dashboard directory * flake8-import-order: Ensure to place project imports last * Make nosehtmloutput an optional test requirement * Add exceptions for action * Added SELECTABLE\_THEMES setting * Use doc8 check * Fix py27dj18 definition * Add '\*.pyc' clean up for unit\_tests * Add Django 1.11 tox env * Use flake8-import-order plugin * doc: cleanup formatting * Horizon Forms should allow themable number spinners * Split out FWaaS dashboard * Split out VPNaaS dashboard * test helper: Ensure to populate JS\_CATALOG in context * Add instance locked status on instances table * HTML test report only when nosehtmloutput is installed * Adds basic angular QoS panel to Horizon * Fix H405 (multi line docstring) warnings (openstack\_dashboard) * hacking: Specify white list rules in noqa explicity * New readonly panel for trunks * Enable some off-by-default hacking rules * Generate unit test HTML reports * Testing: mock add\_panel\_mocks() more flexible * Allow Chrome to start when tox is running npm * Delete instance,volume,etc confirmation is missing name or ID * Update the access\_and\_security url * Handle log message interpolation by the logger * Remove py34 in tox.ini * Drop py34 target in tox.ini * Remove all remaining vendor specific code * hacking: noqa cleanup in openstack\_dashboard * hacking: Drop import\_exceptions from tox.ini * Make extract\_messages quiet in pep8 test * Use ThemableSelectWidget for themable * Correct error in policy action name * Replace six.iteritems/itervalues with dict.items()/values() * Refactor Project Volumes stand-alone panel * Use more specific asserts in tests * Use "Default", "Value" and "DefaultValue" for Heat parameters * Pass environment variables of proxy to tox * Fake deprecated nova client properties * Add default common template to python table views * Adding identity ng-roles panel * Remove duplicated modal\_header statements * Revert "Add policy rules to project panels" * Add policy rules to project panels * Replace six.iteritems(iter) with iter.items() * Fixes inconsistency in Stacks filter label * Wrap api calls with tracing decorators * Add the Profiler panel to the Developer dashboard * Refactor tox & update docs * Remove local/enabled from test settings * Removing deprecate ceilometer code * Removing deprecated Swift UI code * Drop LBaaS v1 dashboard * Server-side filtering Orchestration * Flavors panel can switch to Angular * add ploop type to the list of image backends * Fix logspam in django 1.9 and django 1.10 tests * Make ResourceTypesTests skippable * Using /tmp for SECRET\_KEY in tests * Fix tox cover to not fail * Turn on docs warning check in document generation * Move Developer enabled/ files to contrib * Fix compatibility issues with Django 1.10 * Support for Glance v2 * Remove the lowercase case boolean type in heat param\_type * Enable Python 3.5 testing and fix tests * Fix error detection in horizon test suite * Hard-code stubbed novaclient api\_version to 2.1 * [TrivialFix] Ignore node\_modules while we run flake8 test * Use upper constraints for all jobs in tox.ini * Add filter action to Template Versions tables * Re-work static\_settings to clean up * Remove Orchestration Resource Types names restriction * Add ANGULAR\_FEATURES setting dict * Restrict user private network cidr input * Remove openstack/common from the exclude list of flake8 in tox * Make 'switch' between legacy and Angular Images * Update Django 1.10 tox env * Remove multi select option on Resource types table * Embed support for external data sinks into api.glance * Delete the useless API of heat * Pre-populate the Angular template cache and allow template overrides * Enable tox to run the subset test cases * Do not import local/enabled when to run test * Add Django 1.10 tox env * Escape angularjs templating in unsafe HTML * Fix coverage post job * Updating heat policy file * Implement video capture for failed tests * Handle non ASCII tenant names gracefully when downloading RC file * Use upper-constraints in tox test environments * Whitelist 'rm' in pep8 tox environment * Refactor Orchectration Stacks menu * Update tox.ini for pot files not in tree * [Trivial] Wrong policy for Heat resource type details * Heat Template Versions panel * removing httplib2 test dependency * Add filter function on resource type table * Enhances tox to not rely on run\_tests * Fix remaining Django 1.9 test failures * Pass environment data to update stack action * Remove PYTHONHASHSEED=0 from tox * Make no response notification(msg) level to INFO * Update URLs to Django 1.8+ style * Add "data\_required=True" check into heat rest api * Make ngcontainers the default Swift UI * Branding: Workflow cancel btn should be themable * Horizon support for Heat template versions * Fixing heatclient release compat issues * Add Swift REST API * Add some new heat-snapshot-api * Port last unit tests to Python 3 * Switch to use "# noqa" correctly * Port identity provider dashboard to Python 3 * Follow-up patch of bug: #1527457 * Port dashboard containers to Python 3 * [Django 1.9] Stop using django.utils.importlib * [Django 1.9] Remove testserver from expected\_url * Remove all pre-Django 1.8 workarounds * Add support for identity provider management * [Django 1.9] Replace django.utils.unittest * Port volume backup dashboard to Python 3 * Port loadbalancers tests to Python 3 * Modify 'admin' used by fixed string * Fixed the width of the stack template detail box * Add py27dj19 tox env * Delete some deprecated codes * Correcting heat resource drill down * Use new log style when LOG.error * Make it possible to pick False for default stack parameter value * Add handle get\_file when launch stack from horizon * Eliminate mutable default arguments * Add API services for system information * Add missing unit tests for Heat Stacks Details * Deprecated tox -downloadcache option removed * Delete the unused LOG configure code * Fixes heat templates hidden parameters are not loading problem * Add Developer Dashboard + Bootstrap Theme Preview * Fix errors showing in Network tests * Excising Sahara from Horizon * Force releasenotes warnings to be treated as errors * Make unit testing less reliant on HTML fragments * Adding download for openrc file for keystone v3 * Add version check for listing namespaces * Drop Django 1.7 support * Add reno for release notes management * Add breadcrumb to more details pages * Fix Python 3 issues in dashboard image tests * Port network topology tests to Python 3 * Port dashboard stack tests to Python 3 * Port dashboard router tests on Python 3 * Port project instance tests to Python 3 * tox: add /bin/bash to whitelist\_externals * py3: Fix unicode versus bytes issues * Adds config to disable the password in stack * Wrong url in stack preview detail page * In Stack Details do not display as links events leading to nowhere * Port admin metering tests to Python 3 * py3: Replace unicode with six.text\_type * Replace filter() with list-comprehension * Port remaining Horizon tests for Python 3 * py3: Add json attribute to HTTP response objects * Heat Stacks Details: Events table doesn't show up * Port horizon tabs tests to Python 3 * Port babel\_extract\_angular tests to Python 3 * Add missing MEDIA config in test settings * Fix Python 3 issue in horizon DataTable * Add icons for Heat Stacks BatchActions * Port openstack dashboard overview tests to Python 3 * Port horizon utils tests to Python 3 * Pass Heat environment through form variables too * Improving find static robustness * TestCase: add aliases to assert methods on py3 * Port horizon base tests to Python 3 * Replace SortedDict with OrderedDict * Port dashboard api access tests to Python 3 * Port password tests to Python 3 * Port horizon forms tests to Python 3 * Port error\_pages tests to Python 3 * Port config\_rest\_tests to Python 3 * Run a subset of tests on Python 3 * py3: Replace unicode with six.text\_type * Javascript Heat API * A few fixes to make the App Catalog plugin work * py3: replace reload with six.moves.reload\_module * py3: Replace basestring with six.string\_types * Fixes Selenium tests for openstack\_dashboard * remove django<1.7 from tests * Moving Trove to contrib * Move Horizon to pure plugin loading only * Fix the misspell Ochestration to Orchestration * Remove Router Dashboard * Fix static file paths in heat topology view * Use relative imports * Moving Sahara to contrib * Replace mox with mox3 * Removed JSHint * Switched from JSCS to ESLint * Turning off offline compression for integration test * Removing unnecessary test setting of dashboards * Escape the description param from heat template * Inherit environment variables for tests that use nodeenv * Heat topology display improvement * Establish baseline JSCS configuration * Fix addition of plugin panel to panel group * Fix addition of plugin panel to panel group * [Heat] Resource url mapping improvement * [Launch Instance Fix] Settings for volume name * Revert "Refactoring the exceptions lists" * Refactoring the exceptions lists * [Heat] Fix default values displaying for "Launch Stack" * Fixing changing WEBROOT from / * Use novaclient v2 instead v1\_1 * Heat resource url mapping improvement * Clean modal forms in Orchestration Stacks * Add "Preview Stack" action to Stacks table * Always show stack status reason in topology * Base dashboard Jasmine framework * REST API to support create instance angular (Neutron) * Adding policy rest endpoint for angular * Generate a drop down field for custom heat parameters * Refactor REST API tests for common code patterns * Add orchestration service status reporting * Read access to config via REST * Make the values of Stack Resource status be translatable * Network Rest API for Angular Front End * Cinder REST API for angular front end * Nova REST API for angular front end * Glance Rest API for Angular Front End * Reduce page title duplication in Stacks * Add missing stubs to project stacks tests * Reduced code duplication in creating page headers * Stack resources table improvement * made stack status value is translatable * Move to hacking 0.10 * Make params order fixed in stack forms * Remove unused import urlencode * Make test helpers properly inheritable/extendable * Fixes Resource id mapping with Heat Resources * added filter for stacks table * Add missing OS::Cinder::Volume resource url map * Updating stack resource urls mapping * Heat stack status column improvement * Correcting heat resource page title * Add "Suspend" and "Resume" actions to Stacks table * Resource types table improvement * Keystone REST API for angular front end * Add "Check Stack" action to Stacks table * Orchestration Resource types panel * Unify the syntax used for the {% ... %} delimter in Jinja2 templates * Switch from oslo.utils to oslo\_utils * we don't need to allow insecure any more * REST API for angular front end * Fixed display issues on Details Page caused by bootstrap 3 update * Clean up terminal output for stack details section * Documenting create\_stubs decorator for tests * Fixes Inconsistent usage of Detail / Details * Fixed row deletion for deleted stack * Split large methods into submethods to reduce max-complexity * Create titles without concatenation * Remove Python 2.6 classifier * Stack update call needs password * place the word "Info" with "Information" * 'Stack Template' tab for Heat Stack Details page * Move the panel mocks to a separate method on TestCase * Hide the "Host Aggregates" admin panel if not enabled * Add max-complexity to pep8 for Horizon * Do not log keystone token * Fix E128 errors in remaining openstack\_dashboard/ * Fix E128 errors in openstack\_dashboard/dashboards/project/ * Fix E127 errors in remaining openstack\_dashboard/ * Use strutils from oslo.utils * Fix E127 errors in openstack\_dashboard/dashboards/project/ * Add check for makemessages in tox.ini PEP8 * fixed errors when run\_tests.sh --makemessages * Revert file permissions changed unexpectedly * display boolean and json parameters in stack launch form * Add ':' inside translatable (Part 2 of 2) * Add dropdown actions to all details pages * Workaround for 'File exists' test failure * Remove #noqa from django.conf.urls.include * Fix concatenation in more misc actions * Reuse API client object in a request * Remove #noqa from two common imports and add them to import\_exceptions * swift: Respect the OPENSTACK\_SSL\_NO\_VERIFY setting * Enable flavors metadata update * Use integer instead of string for form max\_length * render stack parameters in the correct order * Allow translators to translate BatchActions * Add templates for bootstrap's horizontal forms * Work toward Python 3.4 support and testing * Render hidden stack parameters with a password field * Display Network panels based on neutron ext-list * Fix order of arguments in assertEqual * Add a config option to disable Router panel * Improve help messages on modals * Horizon changes for DVR * Domain Role assignment to Users * Exclude .ropeproject from flake8 checks * show correct link to compute instances in stack resource overview tab * Separating Identity Dashboard and using RBAC * Remove "builtins = \_" from tox.ini * Run the integration tests headlessly * Replace force\_unicode with force\_text * add Previous link to Project > Stacks table * Delete Stacks button doesn't work * Set python hash seed to 0 in tox.ini * Fix and enable H701 check "Empty localization string" * Fix and enable H702 \_() method check * Re-enable flake8 check * Fix Flake8 style warnings in openstack\_dashboard/ * Update Twitter Bootstrap to version 3 * Cleanup for Horizon fields * Run router dashboard unit tests by default * Fixing subproject test runs * Remove requirements style check * Fix multiple Cross-Site Scripting (XSS) vulnerabilities * Adding support for volume backups * Adding plugins panel for Sahara * Add jshint for javascript linting * Fix keystone warnings while building docs * Ensuring environment data is passed to heat template validate * Use six.add\_metaclass instead of \_\_metaclass\_\_ * Incorrect config item in local\_settings.py.example * Allow extensions to add Angular modules * replace dict.iteritems() with six.iteritems(dict) * Show default text for image format * Remove extraneous vim configuration comments * Added the field label to the field arguments * Use the latest Django 1.4 release to test Horizon * Fix formatting of heat exceptions * Fix tox does not pass arguments to nose * Introduces escaping in Horizon/Orchestration * Add tox env to build docs * Add pagination to stack list * Add logging handler for openstack\_auth in the tests * Plugin-based panel group configuration * Enable settings/change password on keystone v3 * Handle "null" time values for Stacks * Use built-in print() instead of print statement * Fix wrong links to volume detail * Fix project name in the table for Cisco N1K profiles * Remove I18N strings from test/settings.py * Adding django 1.6 support * Updated outdated docstring, fixed typo/space * Adding resource link to resource detail page in Heat view * Additional optional Environment data to heat template selection * Fixing heat stack status column * Heat Stack update view/form * Add flavor extra spec key templates * Sort requirement files in alphabetical order * Integration tests - running the tests * Use six.moves.urllib.parse instead of urlparse * Exclude local directory from flake8 tests * Replace assertEqual(None, \*) with assertIsNone in tests * Adding policy checks for heat * Fixing tests to work with keystoneclient 0.6.0 * Fix missing option in the test settings.py file * Remove delete action and bar when stack is DELETE\_COMPLETE * Fix misspellings in horizon * Remove leftovers of "# noqa" * Sync OPENSTACK\_KEYSTONE\_DEFAULT\_ROLE with keystone * Adding management\_url to test mock client * Passing stack\_id argument to get\_data view * Replace asserts in \_\_init\_\_ files with \_\_all\_\_ * Remove #noqa from most common imports and add them to import\_exceptions * Whitelist external netaddr requirement * Use memoized\_method decorator in views * Updates tox.ini to use new features * Extend options for custom flavor sorting when launching instances * Allow translators to control word order of BatchAction message * Common keystone version fallback * Set can\_set\_mount\_point default to False * Plugin-based dashboard configuration * Update troveclient to 1.0.0 * Heat Stack name is missing form validation * Gate on H4xx docstrings for pep8 * Fix "it's vs. its" typos * Gate on H102 Apache 2.0 license header not found for pep8 * Importing from trove-client compat first * Add option to disable server password fields * Add logging configuration for iso8601 module * Importing from trove-client compat * Fix django.conf.urls.defaults imports * Ignore irrelevant secgroup form field errors * Run flake8 tests automatically on ./run\_tests.sh * Disable H803 check * Remove unused LOG * Convert generator to list in Heat list API call * PEP8 E126 has been resolved * Make troveclient optional * FWaaS Horizon panel * Enable F403 and F999 check * VPNaaS UI implementation * Remove test string from translation files * PEP8 E121 has been resolved * Add Cisco N1K plugin support in Horizon * Make Image Service image formats configurable * Custom CA certificate for verifying SSL connections * Adding panels for trove * Remove encryption option for volumes * Add ceilometer api and the tests for it * Rename heat logical\_resource\_id to resource\_name * Adding RBAC policy system and checks for identity * Show Neutron floating IPs quotas on Overview * Small "H302 check" cleanup * Enable H302 check * Enable H201: do not write "except:" * Fix stack status displaying * Updated from global requirements * Updates Topology to correctly handle resources in the INIT state * Ensure the user monkey patching is done for the API tests * Add support for parsing jsonfied heat error * remove 'import \*' usage (or mark is #noqa) * Removed a comment from tox.ini that no longer applied * Enable pep8 F841 checking * Fixing the AJAX function location * Adding Heat Resource Topology to Horizon * Enable H304 check * Prompt for password on stack create * Adding the ability to set/use domain context * update Quantum references to Neutron * Implementation of a Heat stacks UI * Enable H306 check * Mock some more keystoneclient.Client properties for mox * Fix Further Pyflakes warnings * Enable hacking H101 test * python3: Introduce py33 to tox.ini * Manually mock service\_catalog in stub\_keystoneclient * Sort imports alphabetically * remove unused imports * Add HACKING.rst * Enable most of the pyflakes checks * Add security group rule templates * New tox environment for testing with Django 1.4 * Renames tools/pip-requires to requirements.txt * Group CRUD and Management in Admin Dashboard * Adding CRUD for roles * Adding Domain CRUD in Admin Dashboard * Preparing for Heat tests * Access to heat API via python-heatclient * Move to flake8 * Update Horizon for Django 1.5 compatibility * blueprint quantum-lbaas * Correctly mock keystoneclient.Client.auto\_token property with Mox * Added encryption support for volumes * Resolve API import issues (quantum) * Implements ability to upload local image to glance * Adds API Access information to Access & Security panel * Properly enable/disable project/user edit via setting * Quantum Floating IP support * Ensure to use api.. * Avoid cinder calls, when cinder is unavailable * Fixes for missing files in MANIFEST * Unifies Horizon conf * Client-side validation of password/confirmation match * Allow running tests individually * Enable quota data from multiple sources * Splits OpenStack Dashboard bits from framework app code * Provide utilities to automate secure secret key generation * Add ProjectTestingInterface to horizon * Revamp of testing machinery ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4774961 heat_dashboard-16.0.0/HACKING.rst0000664000175000017500000000023715257164522015161 0ustar00zuulzuulopenstack Style Commandments =============================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4774961 heat_dashboard-16.0.0/LICENSE0000664000175000017500000002363715257164522014401 0ustar00zuulzuul Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.434259 heat_dashboard-16.0.0/PKG-INFO0000644000175000017500000000370315257164606014462 0ustar00zuulzuulMetadata-Version: 2.4 Name: heat-dashboard Version: 16.0.0 Summary: Heat Management Dashboard Home-page: https://docs.openstack.org/heat-dashboard/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3.14 Requires-Python: >=3.11 License-File: LICENSE Requires-Dist: pbr>=5.5.0 Requires-Dist: python-heatclient>=1.10.0 Requires-Dist: horizon>=27.0.0 Requires-Dist: xstatic-angular-uuid>=0.0.4.0 Requires-Dist: xstatic-angular-vis>=4.16.0.0 Requires-Dist: xstatic-filesaver>=1.3.2.0 Requires-Dist: xstatic-js-yaml>=3.8.1.0 Requires-Dist: xstatic-json2yaml>=0.1.1.0 Dynamic: author Dynamic: author-email Dynamic: classifier Dynamic: description Dynamic: home-page Dynamic: license-file Dynamic: requires-dist Dynamic: requires-python Dynamic: summary ========================== Welcome to Heat Dashboard! ========================== .. image:: https://governance.openstack.org/tc/badges/heat-dashboard.svg .. Change things from this point on Heat dashboard is a horizon plugin for Heat. * License: Apache license * Documentation: https://docs.openstack.org/heat-dashboard/latest/ * Source: https://opendev.org/openstack/heat-dashboard * Bugs: https://storyboard.openstack.org/#!/project/openstack/heat-dashboard * Release Notes: https://docs.openstack.org/releasenotes/heat-dashboard ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4784963 heat_dashboard-16.0.0/README.rst0000664000175000017500000000103015257164522015042 0ustar00zuulzuul========================== Welcome to Heat Dashboard! ========================== .. image:: https://governance.openstack.org/tc/badges/heat-dashboard.svg .. Change things from this point on Heat dashboard is a horizon plugin for Heat. * License: Apache license * Documentation: https://docs.openstack.org/heat-dashboard/latest/ * Source: https://opendev.org/openstack/heat-dashboard * Bugs: https://storyboard.openstack.org/#!/project/openstack/heat-dashboard * Release Notes: https://docs.openstack.org/releasenotes/heat-dashboard ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6614952 heat_dashboard-16.0.0/babel-django.cfg0000664000175000017500000000022515257164523016347 0ustar00zuulzuul[python: **.py] [django: **/templates/**.html] [django: **/templates/**.csv] [extractors] django = horizon.utils.babel_extract_django:extract_django ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4794962 heat_dashboard-16.0.0/babel-djangojs.cfg0000664000175000017500000000017415257164522016706 0ustar00zuulzuul[javascript: **.js] [angular: **/static/**.html] [extractors] angular = horizon.utils.babel_extract_angular:extract_angular ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4794962 heat_dashboard-16.0.0/bindep.txt0000664000175000017500000000070215257164522015362 0ustar00zuulzuul# This is a cross-platform list tracking distribution packages needed for install and tests; # see https://docs.openstack.org/infra/bindep/ for additional information. libffi-dev [platform:dpkg test] libffi-devel [platform:rpm test] # selenium tests firefox [selenium platform:ubuntu] firefox-esr [selenium platform:debian] xvfb [selenium platform:dpkg] # already part of xorg-x11-server on openSUSE xorg-x11-server-Xvfb [selenium platform:redhat] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2892623 heat_dashboard-16.0.0/devstack/0000775000175000017500000000000015257164606015170 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4804962 heat_dashboard-16.0.0/devstack/plugin.sh0000664000175000017500000000377115257164522017027 0ustar00zuulzuul# plugin.sh - DevStack plugin.sh dispatch script heat-dashboard HEAT_DASHBOARD_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd) function install_heat_dashboard { setup_develop ${HEAT_DASHBOARD_DIR} } function configure_heat_dashboard { cp -a ${HEAT_DASHBOARD_DIR}/heat_dashboard/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/ cp -a ${HEAT_DASHBOARD_DIR}/heat_dashboard/local_settings.d/_1699_orchestration_settings.py ${DEST}/horizon/openstack_dashboard/local/local_settings.d/ cp -a ${HEAT_DASHBOARD_DIR}/heat_dashboard/conf/heat_policy.yaml ${DEST}/horizon/openstack_dashboard/conf/ cp -a ${HEAT_DASHBOARD_DIR}/heat_dashboard/conf/default_policies/heat.yaml ${DEST}/horizon/openstack_dashboard/conf/default_policies # NOTE: If locale directory does not exist, compilemessages will fail, # so check for an existence of locale directory is required. if [ -d ${HEAT_DASHBOARD_DIR}/heat_dashboard/locale ]; then (cd ${HEAT_DASHBOARD_DIR}/heat_dashboard; DJANGO_SETTINGS_MODULE=openstack_dashboard.settings $PYTHON ../manage.py compilemessages) fi } # check for service enabled if is_service_enabled heat-dashboard; then if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then # Set up system services # no-op : elif [[ "$1" == "stack" && "$2" == "install" ]]; then # Perform installation of service source echo_summary "Installing Heat Dashboard" install_heat_dashboard elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then # Configure after the other layer 1 and 2 services have been configured echo_summary "Configuring Heat Dashboard" configure_heat_dashboard elif [[ "$1" == "stack" && "$2" == "extra" ]]; then # no-op : fi if [[ "$1" == "unstack" ]]; then # no-op : fi if [[ "$1" == "clean" ]]; then # Remove state and transient data # Remember clean.sh first calls unstack.sh # no-op : fi fi ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4804962 heat_dashboard-16.0.0/devstack/settings0000664000175000017500000000011015257164522016740 0ustar00zuulzuul# settings file for heat-dashboard plugin enable_service heat-dashboard ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2892623 heat_dashboard-16.0.0/doc/0000775000175000017500000000000015257164606014131 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4814963 heat_dashboard-16.0.0/doc/requirements.txt0000664000175000017500000000020015257164522017402 0ustar00zuulzuulopenstackdocstheme>=2.2.1 # Apache-2.0 sphinx>=2.0.0 # BSD reno>=3.1.0 # Apache-2.0 sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2902622 heat_dashboard-16.0.0/doc/source/0000775000175000017500000000000015257164606015431 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4824963 heat_dashboard-16.0.0/doc/source/conf.py0000664000175000017500000000612715257164522016733 0ustar00zuulzuul# 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. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'openstackdocstheme', # 'sphinx.ext.intersphinx', 'sphinxcontrib.rsvgconverter', ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Heat Dashboard' copyright = '2017, OpenStack Developers' # openstackdocstheme options openstackdocs_repo_name = 'openstack/heat-dashboard' openstackdocs_pdf_link = True openstackdocs_use_storyboard = True openstackdocs_auto_name = False # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] html_theme = 'openstackdocs' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'doc-heat-dashboard.tex', 'Heat Dashboard Documentation', 'OpenStack Developers', 'manual', True), ] latex_domain_indices = False latex_elements = { # Additional stuff for the LaTeX preamble. # openany: Skip blank pages in generated PDFs 'extraclassoptions': 'openany,oneside', 'makeindex': '', 'printindex': '', 'preamble': r'\setcounter{tocdepth}{3}', } man_pages = [ ('index', 'Heat Dashboard Documentation', 'Documentation for Heat Dashboard plugin to Openstack\ Dashboard (Horizon)', ['OpenStack'], 1) ] # Example configuration for intersphinx: refer to the Python standard library. # intersphinx_mapping = {'http://docs.python.org/': None} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2902622 heat_dashboard-16.0.0/doc/source/configuration/0000775000175000017500000000000015257164606020300 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4834962 heat_dashboard-16.0.0/doc/source/configuration/configuration.rst0000664000175000017500000000147715257164522023707 0ustar00zuulzuul============= Configuration ============= Heat Dashboard has configuration option as below. For more configurations, see `Configuration Guide `_ in the Horizon documentation. OPENSTACK_HEAT_STACK ~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 9.0.0(Mitaka) Default: .. code-block:: python { 'enable_user_pass': True } A dictionary of settings to use with heat stacks. Currently, the only setting available is "enable_user_pass", which can be used to disable the password field while launching the stack. Currently HEAT API needs user password to perform all the heat operations because in HEAT API trusts is not enabled by default. So, this setting can be set as "False" in-case HEAT uses trusts by default otherwise it needs to be set as "True". ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4834962 heat_dashboard-16.0.0/doc/source/configuration/index.rst0000664000175000017500000000023115257164522022132 0ustar00zuulzuul================================== Heat Dashboard configuration guide ================================== .. toctree:: :maxdepth: 1 configuration ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2912621 heat_dashboard-16.0.0/doc/source/contributor/0000775000175000017500000000000015257164606020003 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4834962 heat_dashboard-16.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000366015257164522023246 0ustar00zuulzuul============================ So You Want to Contribute... ============================ For general information on contributing to OpenStack, please check out the `contributor guide `_ to get started. It covers all the basics that are common to all OpenStack projects: the accounts you need, the basics of interacting with our Gerrit review system, how we communicate as a community, etc. Below will cover the more project specific information you need to get started with heat-dashboard. Communication ~~~~~~~~~~~~~ * IRC channel #heat at OFTC * Mailing list (prefix subjects with ``[heat]`` for faster responses) http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss Contacting the Core Team ~~~~~~~~~~~~~~~~~~~~~~~~ Please refer the `heat-dashboard Core Team `_ contacts. New Feature Planning ~~~~~~~~~~~~~~~~~~~~ heat-dashboard features are tracked on `Storyboard `_. Task Tracking ~~~~~~~~~~~~~ We track our tasks in `Storyboard `_. If you're looking for some smaller, easier work item to pick up and get started on, search for the 'low-hanging-fruit' tag. Reporting a Bug ~~~~~~~~~~~~~~~ You found an issue and want to make sure we are aware of it? You can do so on `Storyboard `_. Getting Your Patch Merged ~~~~~~~~~~~~~~~~~~~~~~~~~ All changes proposed to the heat-dashboard project require one or two +2 votes from heat-dashboard core reviewers before one of the core reviewers can approve patch by giving ``Workflow +1`` vote. Project Team Lead Duties ~~~~~~~~~~~~~~~~~~~~~~~~ All common PTL duties are enumerated in the `PTL guide `_. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4834962 heat_dashboard-16.0.0/doc/source/contributor/devstack.rst0000664000175000017500000000052415257164522022337 0ustar00zuulzuul============================== Use Heat Dashboard in DevStack ============================== Set up your ``local.conf`` to enable heat-dashboard:: [[local|localrc]] enable_plugin heat-dashboard https://opendev.org/openstack/heat-dashboard .. note:: You also need to install Heat itself into DevStack to use Heat Dashboard. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4834962 heat_dashboard-16.0.0/doc/source/contributor/index.rst0000664000175000017500000000021615257164522021640 0ustar00zuulzuul=========================== Contributor Documentation =========================== .. toctree:: :maxdepth: 2 contributing devstack ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4834962 heat_dashboard-16.0.0/doc/source/index.rst0000664000175000017500000000124015257164522017264 0ustar00zuulzuul.. openstack documentation master file, created by sphinx-quickstart on Tue Jul 9 22:26:36 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. the main title comes from README.rst .. include:: ../../README.rst Contents -------- .. toctree:: :maxdepth: 2 Installation Guide Configuration Guide User Documentation contributor/index For Contributors ---------------- * If you are a new contributor to heat-dashboard please refer: :doc:`contributor/contributing` .. toctree:: :hidden: contributor/contributing ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2912621 heat_dashboard-16.0.0/doc/source/install/0000775000175000017500000000000015257164606017077 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.484496 heat_dashboard-16.0.0/doc/source/install/index.rst0000664000175000017500000000516715257164522020746 0ustar00zuulzuul================================= Heat Dashboard installation guide ================================= This page describes the manual installation of heat-dashboard, while distribution packages may provide more automated process. .. note:: This page assumes horizon has been installed. Horizon setup is beyond the scope of this page. Install Heat Dashboard with all relevant packages to your Horizon environment. .. code-block:: console pip install heat-dashboard In most cases, heat-dashboard is installed into your python "site-packages" directory like ``/usr/local/lib/python2.7/site-packages``. We refer to the directory of heat-dashboard as ```` below and it would be ``/heat_dashboard`` if installed via pip. The path varies depending on Linux distribution you use. To enable heat-dashboard plugin, you need to put horizon plugin setup files into horizon "enabled" directory. The plugin setup files are found in ``/enabled``. .. code-block:: console $ cp /enabled/_[1-9]*.py \ /usr/share/openstack-dashboard/openstack_dashboard/local/enabled .. note:: The directory ``local/enabled`` may be different depending on your environment or distribution used. The path above is one used in Ubuntu horizon package. Configure the policy file for heat-dashboard in OpenStack Dashboard ``local_settings.py``. .. code-block:: python POLICY_FILES['orchestration'] = '/conf/heat_policy.yaml' .. note:: If your ``local_settings.py`` has no ``POLICY_FILES`` yet, you need to define the default ``POLICY_FILES`` in ``local_settings.py``. If you use the example ``local_settings.py`` file from horizon, what you need is to uncomment ``POLICY_FILES`` (which contains the default values). You can also add additional configurations to ``local_settings.py``. For more detail, see :doc:`/configuration/configuration`. You can also find an example file at ``/heat_dashboard/local_settings.d``. Compile the translation message catalogs of heat-dashboard. .. code-block:: console $ cd $ python ./manage.py compilemessages Run the Django update commands. Note that ``compress`` is required when you enable compression. .. code-block:: console $ cd $ DJANGO_SETTINGS_MODULE=openstack_dashboard.settings python manage.py collectstatic --noinput $ DJANGO_SETTINGS_MODULE=openstack_dashboard.settings python manage.py compress --force Finally, restart your web server. For example, in case of apache: .. code-block:: console $ sudo systemctl restart apache2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2922623 heat_dashboard-16.0.0/doc/source/user/0000775000175000017500000000000015257164606016407 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.484496 heat_dashboard-16.0.0/doc/source/user/index.rst0000664000175000017500000000025515257164522020247 0ustar00zuulzuul================================= Heat Dashboard User Documentation ================================= .. toctree:: :maxdepth: 2 stacks.rst template_generator.rst ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.484496 heat_dashboard-16.0.0/doc/source/user/stacks.rst0000664000175000017500000001616015257164522020432 0ustar00zuulzuul======================== Launch and manage stacks ======================== OpenStack Orchestration is a service that you can use to orchestrate multiple composite cloud applications. This service supports the use of both the Amazon Web Services (AWS) CloudFormation template format through both a Query API that is compatible with CloudFormation and the native OpenStack Heat Orchestration Template (HOT) format through a REST API. These flexible template languages enable application developers to describe and automate the deployment of infrastructure, services, and applications. The templates enable creation of most OpenStack resource types, such as instances, floating IP addresses, volumes, security groups, and users. Once created, the resources are referred to as stacks. The template languages are described in the `Template Guide `_. Launch a stack ~~~~~~~~~~~~~~ #. Log in to the dashboard. #. Select the appropriate project from the drop down menu at the top left. #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and click :guilabel:`Stacks` category. #. Click :guilabel:`Launch Stack`. #. In the :guilabel:`Select Template` dialog box, specify the following values: +---------------------------------------+-------------------------------+ | :guilabel:`Template Source` | Choose the source of the | | | template from the list. | +---------------------------------------+-------------------------------+ | :guilabel:`Template URL/File/Data` | Depending on the source that | | | you select, enter the URL, | | | browse to the file location, | | | or directly include the | | | template. | +---------------------------------------+-------------------------------+ | :guilabel:`Environment Source` | Choose the source of the | | | environment from the list. | | | The environment files contain | | | additional settings for the | | | stack. | +---------------------------------------+-------------------------------+ | :guilabel:`Environment File/Data` | Depending on the source that | | | you select, browse to the | | | file location, directly | | | include the environment | +---------------------------------------+-------------------------------+ #. Click :guilabel:`Next`. #. In the :guilabel:`Launch Stack` dialog box, specify the following values: +---------------------------------+---------------------------------+ | :guilabel:`Stack Name` | Enter a name to identify | | | the stack. | +---------------------------------+---------------------------------+ | :guilabel:`Creation Timeout` | Specify the number of minutes | | :guilabel:`(minutes)` | that can elapse before the | | | launch of the stack times out. | +---------------------------------+---------------------------------+ | :guilabel:`Rollback On Failure` | Select this check box if you | | | want the service to roll back | | | changes if the stack fails to | | | launch. | +---------------------------------+---------------------------------+ | :guilabel:`Password for user` | Specify the password that | | :guilabel:`"demo"` | the default user uses when the | | | stack is created. | +---------------------------------+---------------------------------+ | :guilabel:`DBUsername` | Specify the name of the | | | database user. | +---------------------------------+---------------------------------+ | :guilabel:`LinuxDistribution` | Specify the Linux distribution | | | that is used in the stack. | +---------------------------------+---------------------------------+ | :guilabel:`DBRootPassword` | Specify the root password for | | | the database. | +---------------------------------+---------------------------------+ | :guilabel:`KeyName` | Specify the name of the key pair| | | to use to log in to the stack. | +---------------------------------+---------------------------------+ | :guilabel:`DBName` | Specify the name of the | | | database. | +---------------------------------+---------------------------------+ | :guilabel:`DBPassword` | Specify the password of the | | | database. | +---------------------------------+---------------------------------+ | :guilabel:`InstanceType` | Specify the flavor for the | | | instance. | +---------------------------------+---------------------------------+ #. Click :guilabel:`Launch` to create a stack. The :guilabel:`Stacks` tab shows the stack. After the stack is created, click on the stack name to see the following details: Topology The topology of the stack. Overview The parameters and details of the stack. Resources The resources used by the stack. Events The events related to the stack. Template The template for the stack. Manage a stack ~~~~~~~~~~~~~~ #. Log in to the dashboard. #. Select the appropriate project from the drop down menu at the top left. #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and click :guilabel:`Stacks` category. #. Select the stack that you want to update. #. Click :guilabel:`Change Stack Template`. #. In the :guilabel:`Select Template` dialog box, select the new template source or environment source. #. Click :guilabel:`Next`. The :guilabel:`Update Stack Parameters` window appears. #. Enter new values for any parameters that you want to update. #. Click :guilabel:`Update`. Delete a stack ~~~~~~~~~~~~~~ When you delete a stack, you cannot undo this action. #. Log in to the dashboard. #. Select the appropriate project from the drop down menu at the top left. #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and click :guilabel:`Stacks` category. #. Select the stack that you want to delete. #. Click :guilabel:`Delete Stack`. #. In the confirmation dialog box, click :guilabel:`Delete Stack` to confirm the deletion. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.484496 heat_dashboard-16.0.0/doc/source/user/template_generator.rst0000664000175000017500000000353115257164522023021 0ustar00zuulzuul====================================== Generate a Heat Orchestration Template ====================================== Heat Dashboard provides a user-friendly interface to generate Heat Orchestration templates in a Drag and Drop way. Generate a template ~~~~~~~~~~~~~~~~~~~ #. Log in to the dashboard. #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and click :guilabel:`Template Generator` category. #. Wait until the page is completely loaded. It may take several seconds. #. Click the dropdown menu of Template Version, and choose an appropriate version. #. Drag icons of resource types at the top of the page to the central canvas. #. Click icons on the canvas to specify properties of resources. #. Click EDIT button at the top of the canvas, to enable manipulate mode. #. When in manipulate mode, click on CONNECT button to add an edge between icons. #. Click edges to show details of connections. #. Click the Generate Template button at the top-right of the page and generated template will be shown in a text box. You can also add modification to the template here. #. Click CREATE STACK to jump to continue to :guilabel:`Launch Stack`. #. Click DOWNLOAD STACK to download the generated template. #. You can also click the Manage Drafts button at the top-right of the page, to temporarily save the editing canvas or to load a saved one. Currently Supported resource types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 types of resources are supported in the first release of Heat Dashboard. #. OS::Cinder::Volume #. OS::Cinder::VolumeAttachment #. OS::Heat::ResourceGroup #. OS::Neutron::FloatingIP #. OS::Neutron::FloatingIPAssociation #. OS::Neutron::Net #. OS::Neutron::Port #. OS::Neutron::Router #. OS::Neutron::RouterInterface #. OS::Neutron::SecurityGroup #. OS::Neutron::Subnet #. OS::Nova::KeyPair #. OS::Nova::Server ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2922623 heat_dashboard-16.0.0/heat_dashboard/0000775000175000017500000000000015257164606016314 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.484496 heat_dashboard-16.0.0/heat_dashboard/__init__.py0000664000175000017500000000000015257164522020410 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.295262 heat_dashboard-16.0.0/heat_dashboard/api/0000775000175000017500000000000015257164606017065 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4854963 heat_dashboard-16.0.0/heat_dashboard/api/__init__.py0000664000175000017500000000300415257164522021170 0ustar00zuulzuul# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, Inc. # Copyright 2013 Big Switch Networks # # 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. """ Methods and interface objects used to interact with external APIs. API method calls return objects that are in many cases objects with attributes that are direct maps to the data returned from the API http call. Unfortunately, these objects are also often constructed dynamically, making it difficult to know what data is available from the API object. Because of this, all API calls should wrap their returned object in one defined here, using only explicitly defined attributes and/or methods. In other words, Horizon developers not working on openstack_dashboard.api shouldn't need to understand the finer details of APIs for Keystone/Nova/Glance/Swift et. al. """ from heat_dashboard.api import heat __all__ = [ "heat", ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4854963 heat_dashboard-16.0.0/heat_dashboard/api/heat.py0000664000175000017500000002135115257164522020357 0ustar00zuulzuul# 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. import contextlib from urllib import request from django.conf import settings from oslo_serialization import jsonutils from heatclient import client as heat_client from heatclient.common import template_format from heatclient.common import template_utils from heatclient.common import utils as heat_utils from horizon import exceptions from horizon.utils import functions as utils from horizon.utils.memoized import memoized from openstack_dashboard.api import base from openstack_dashboard.contrib.developer.profiler import api as profiler def format_parameters(params): parameters = {} for count, p in enumerate(params, 1): parameters['Parameters.member.%d.ParameterKey' % count] = p parameters['Parameters.member.%d.ParameterValue' % count] = params[p] return parameters @memoized def heatclient(request, password=None): api_version = "1" insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) endpoint = base.url_for(request, 'orchestration') kwargs = { 'token': request.user.token.id, 'insecure': insecure, 'ca_file': cacert, 'username': request.user.username, 'password': password # 'timeout': args.timeout, # 'ca_file': args.ca_file, # 'cert_file': args.cert_file, # 'key_file': args.key_file, } client = heat_client.Client(api_version, endpoint, **kwargs) client.format_parameters = format_parameters return client @profiler.trace def stacks_list(request, marker=None, sort_dir='desc', sort_key='created_at', paginate=False, filters=None): limit = getattr(settings, 'API_RESULT_LIMIT', 1000) page_size = utils.get_page_size(request) if paginate: request_size = page_size + 1 else: request_size = limit kwargs = {'sort_dir': sort_dir, 'sort_key': sort_key} if marker: kwargs['marker'] = marker if filters: kwargs.update(filters) if 'status' in kwargs: kwargs['status'] = kwargs['status'].replace(' ', '_').upper() stacks_iter = heatclient(request).stacks.list(limit=request_size, **kwargs) has_prev_data = False has_more_data = False stacks = list(stacks_iter) if paginate: if len(stacks) > page_size: stacks.pop() has_more_data = True if marker is not None: has_prev_data = True elif sort_dir == 'asc' and marker is not None: has_more_data = True elif marker is not None: has_prev_data = True return (stacks, has_more_data, has_prev_data) def _ignore_if(key, value): if key != 'get_file' and key != 'type': return True if not isinstance(value, str): return True if (key == 'type' and not value.endswith(('.yaml', '.template'))): return True return False @profiler.trace def get_template_files(template_data=None, template_url=None, files=None): if template_data: tpl = template_data elif template_url: with contextlib.closing(request.urlopen(template_url)) as u: tpl = u.read() else: return {}, None if not tpl: return {}, None if isinstance(tpl, bytes): tpl = tpl.decode('utf-8') template = template_format.parse(tpl) if files is None: files = {} _get_file_contents(template, files) return files, template def _get_file_contents(from_data, files): if not isinstance(from_data, (dict, list)): return if isinstance(from_data, dict): recurse_data = from_data.values() for key, value in from_data.items(): if _ignore_if(key, value): continue if value in files: continue if not value.startswith(('http://', 'https://')): raise exceptions.GetFileError(value, 'get_file') if value not in files: file_content = heat_utils.read_url_content(value) if template_utils.is_template(file_content): template = get_template_files(template_url=value)[1] file_content = jsonutils.dumps(template) files[value] = file_content else: recurse_data = from_data for value in recurse_data: _get_file_contents(value, files) @profiler.trace def stack_delete(request, stack_id): return heatclient(request).stacks.delete(stack_id) @profiler.trace def stack_get(request, stack_id): return heatclient(request).stacks.get(stack_id) @profiler.trace def template_get(request, stack_id): return heatclient(request).stacks.template(stack_id) @profiler.trace def stack_create(request, password=None, **kwargs): return heatclient(request, password).stacks.create(**kwargs) @profiler.trace def stack_preview(request, password=None, **kwargs): return heatclient(request, password).stacks.preview(**kwargs) @profiler.trace def stack_update(request, stack_id, password=None, **kwargs): return heatclient(request, password).stacks.update(stack_id, **kwargs) @profiler.trace def snapshot_create(request, stack_id): return heatclient(request).stacks.snapshot(stack_id) @profiler.trace def snapshot_list(request, stack_id): return heatclient(request).stacks.snapshot_list(stack_id) @profiler.trace def snapshot_show(request, stack_id, snapshot_id): return heatclient(request).stacks.snapshot_show(stack_id, snapshot_id) @profiler.trace def snapshot_delete(request, stack_id, snapshot_id): return heatclient(request).stacks.snapshot_delete(stack_id, snapshot_id) @profiler.trace def events_list(request, stack_name, marker=None, sort_dir='desc', sort_key='event_time', paginate=False, filters=None): limit = getattr(settings, 'API_RESULT_LIMIT', 1000) page_size = utils.get_page_size(request) if paginate: request_size = page_size + 1 else: request_size = limit kwargs = {'sort_dir': sort_dir, 'sort_key': sort_key} if marker: kwargs['marker'] = marker if filters: kwargs.update(filters) events_iter = heatclient(request).events.list(stack_name, limit=request_size, **kwargs) has_prev_data = False has_more_data = False events = list(events_iter) if paginate: if len(events) > page_size: events.pop() has_more_data = True if marker is not None: has_prev_data = True elif sort_dir == 'asc' and marker is not None: has_more_data = True elif marker is not None: has_prev_data = True return events, has_more_data, has_prev_data @profiler.trace def resources_list(request, stack_name): return heatclient(request).resources.list(stack_name) @profiler.trace def resource_get(request, stack_id, resource_name): return heatclient(request).resources.get(stack_id, resource_name) @profiler.trace def resource_metadata_get(request, stack_id, resource_name): return heatclient(request).resources.metadata(stack_id, resource_name) @profiler.trace def template_validate(request, **kwargs): return heatclient(request).stacks.validate(**kwargs) @profiler.trace def action_check(request, stack_id): return heatclient(request).actions.check(stack_id) @profiler.trace def action_suspend(request, stack_id): return heatclient(request).actions.suspend(stack_id) @profiler.trace def action_resume(request, stack_id): return heatclient(request).actions.resume(stack_id) @profiler.trace def resource_types_list(request, filters=None): return heatclient(request).resource_types.list(filters=filters) @profiler.trace def resource_type_get(request, resource_type): return heatclient(request).resource_types.get(resource_type) @profiler.trace def service_list(request): return heatclient(request).services.list() @profiler.trace def template_version_list(request): return heatclient(request).template_versions.list() @profiler.trace def template_function_list(request, template_version): return heatclient(request).template_versions.get(template_version) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2962623 heat_dashboard-16.0.0/heat_dashboard/api/rest/0000775000175000017500000000000015257164606020042 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4854963 heat_dashboard-16.0.0/heat_dashboard/api/rest/__init__.py0000664000175000017500000000176415257164522022160 0ustar00zuulzuul# Copyright 2014, Rackspace, US, 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. """This package holds the REST API that supports the Horizon dashboard Javascript code. It is not intended to be used outside of Horizon, and makes no promises of stability or fitness for purpose outside of that scope. It does not promise to adhere to the general OpenStack API Guidelines set out in https://wiki.openstack.org/wiki/APIChangeGuidelines. """ from heat_dashboard.api.rest import heat __all__ = [ 'heat', ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4854963 heat_dashboard-16.0.0/heat_dashboard/api/rest/heat.py0000664000175000017500000000333015257164522021331 0ustar00zuulzuul# 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. """API for the heat service.""" from django.views import generic from heat_dashboard import api from openstack_dashboard import api as dashboard_api from openstack_dashboard.api.rest import urls from openstack_dashboard.api.rest import utils as rest_utils @urls.register class Validate(generic.View): """API for validating a template""" url_regex = r'heat/validate/$' @rest_utils.ajax(data_required=True) def post(self, request): """Validate a template The following parameters may be passed in the POST application/json object. The parameters are: request: :param template_url: The template to validate """ return api.heat.template_validate(request, **(request.DATA)) @urls.register class Services(generic.View): """API for heat services.""" url_regex = r'heat/services/$' @rest_utils.ajax() def get(self, request): """Get a list of heat services.""" if dashboard_api.base.is_service_enabled(request, 'orchestration'): result = api.heat.service_list(request) return {'items': [u.to_dict() for u in result]} else: raise rest_utils.AjaxError(501, '') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2962623 heat_dashboard-16.0.0/heat_dashboard/conf/0000775000175000017500000000000015257164606017241 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2962623 heat_dashboard-16.0.0/heat_dashboard/conf/default_policies/0000775000175000017500000000000015257164606022554 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4854963 heat_dashboard-16.0.0/heat_dashboard/conf/default_policies/heat.yaml0000664000175000017500000007121715257164522024366 0ustar00zuulzuul- check_str: (role:admin and is_admin_project:True) OR (role:admin and system_scope:all) description: Decides what is required for the 'is_admin:True' check to succeed. name: context_is_admin operations: [] scope_types: null - check_str: role:admin description: Default rule for project admin. name: project_admin operations: [] scope_types: null - check_str: not role:heat_stack_user description: Default rule for deny stack user. name: deny_stack_user operations: [] scope_types: null - check_str: '!' description: Default rule for deny everybody. name: deny_everybody operations: [] scope_types: null - check_str: '' description: Default rule for allow everybody. name: allow_everybody operations: [] scope_types: null - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:action deprecated_since: null description: Performs non-lifecycle operations on the stack (Snapshot, Resume, Cancel update, or check stack resources). This is the default for all actions but can be overridden by more specific policies for individual actions. name: actions:action operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:snapshot deprecated_since: null description: Create stack snapshot name: actions:snapshot operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:suspend deprecated_since: null description: Suspend a stack. name: actions:suspend operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:resume deprecated_since: null description: Resume a suspended stack. name: actions:resume operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:check deprecated_since: null description: Check stack resources. name: actions:check operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:cancel_update deprecated_since: null description: Cancel stack operation and roll back. name: actions:cancel_update operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: actions:cancel_without_rollback deprecated_since: null description: Cancel stack operation without rolling back. name: actions:cancel_without_rollback operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: build_info:build_info deprecated_since: null description: Show build information. name: build_info:build_info operations: - method: GET path: /v1/{tenant_id}/build_info scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:ListStacks deprecated_since: null description: null name: cloudformation:ListStacks operations: [] scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:CreateStack deprecated_since: null description: null name: cloudformation:CreateStack operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:DescribeStacks deprecated_since: null description: null name: cloudformation:DescribeStacks operations: [] scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:DeleteStack deprecated_since: null description: null name: cloudformation:DeleteStack operations: [] scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:UpdateStack deprecated_since: null description: null name: cloudformation:UpdateStack operations: [] scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:CancelUpdateStack deprecated_since: null description: null name: cloudformation:CancelUpdateStack operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:DescribeStackEvents deprecated_since: null description: null name: cloudformation:DescribeStackEvents operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:ValidateTemplate deprecated_since: null description: null name: cloudformation:ValidateTemplate operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:GetTemplate deprecated_since: null description: null name: cloudformation:GetTemplate operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:EstimateTemplateCost deprecated_since: null description: null name: cloudformation:EstimateTemplateCost operations: [] scope_types: - project - check_str: (role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s) deprecated_reason: null deprecated_rule: check_str: rule:allow_everybody name: cloudformation:DescribeStackResource deprecated_since: null description: null name: cloudformation:DescribeStackResource operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:DescribeStackResources deprecated_since: null description: null name: cloudformation:DescribeStackResources operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: cloudformation:ListStackResources deprecated_since: null description: null name: cloudformation:ListStackResources operations: [] scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: events:index deprecated_since: null description: List events. name: events:index operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/events scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: events:show deprecated_since: null description: Show event. name: events:show operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/events/{event_id} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: resource:index deprecated_since: null description: List resources. name: resource:index operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources scope_types: - project - check_str: (role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s) deprecated_reason: null deprecated_rule: check_str: rule:allow_everybody name: resource:metadata deprecated_since: null description: Show resource metadata. name: resource:metadata operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/metadata scope_types: - project - check_str: (role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s) deprecated_reason: null deprecated_rule: check_str: rule:allow_everybody name: resource:signal deprecated_since: null description: Signal resource. name: resource:signal operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/signal scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: resource:mark_unhealthy deprecated_since: null description: Mark resource as unhealthy. name: resource:mark_unhealthy operations: - method: PATCH path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name_or_physical_id} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: resource:show deprecated_since: null description: Show resource. name: resource:show operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name} scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Nova::Flavor operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Cinder::EncryptedVolumeType operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Cinder::VolumeType operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Cinder::Quota operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::Quota operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Nova::Quota operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Octavia::Quota operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Manila::ShareType operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::ProviderNet operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::QoSPolicy operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::QoSBandwidthLimitRule operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::QoSDscpMarkingRule operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::QoSMinimumBandwidthRule operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::QoSMinimumPacketRateRule operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Neutron::Segment operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Nova::HostAggregate operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Cinder::QoSSpecs operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Cinder::QoSAssociation operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Keystone::* operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Blazar::Host operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Octavia::Flavor operations: [] scope_types: - project - check_str: rule:project_admin description: null name: resource_types:OS::Octavia::FlavorProfile operations: [] scope_types: - project - check_str: role:admin and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:context_is_admin name: service:index deprecated_since: null description: null name: service:index operations: [] scope_types: - project - check_str: rule:deny_everybody description: List configs globally. name: software_configs:global_index operations: - method: GET path: /v1/{tenant_id}/software_configs scope_types: null - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_configs:index deprecated_since: null description: List configs. name: software_configs:index operations: - method: GET path: /v1/{tenant_id}/software_configs scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_configs:create deprecated_since: null description: Create config. name: software_configs:create operations: - method: POST path: /v1/{tenant_id}/software_configs scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_configs:show deprecated_since: null description: Show config details. name: software_configs:show operations: - method: GET path: /v1/{tenant_id}/software_configs/{config_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_configs:delete deprecated_since: null description: Delete config. name: software_configs:delete operations: - method: DELETE path: /v1/{tenant_id}/software_configs/{config_id} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_deployments:index deprecated_since: null description: List deployments. name: software_deployments:index operations: - method: GET path: /v1/{tenant_id}/software_deployments scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_deployments:create deprecated_since: null description: Create deployment. name: software_deployments:create operations: - method: POST path: /v1/{tenant_id}/software_deployments scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_deployments:show deprecated_since: null description: Show deployment details. name: software_deployments:show operations: - method: GET path: /v1/{tenant_id}/software_deployments/{deployment_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_deployments:update deprecated_since: null description: Update deployment. name: software_deployments:update operations: - method: PUT path: /v1/{tenant_id}/software_deployments/{deployment_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: software_deployments:delete deprecated_since: null description: Delete deployment. name: software_deployments:delete operations: - method: DELETE path: /v1/{tenant_id}/software_deployments/{deployment_id} scope_types: - project - check_str: (role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s) description: Show server configuration metadata. name: software_deployments:metadata operations: - method: GET path: /v1/{tenant_id}/software_deployments/metadata/{server_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:abandon deprecated_since: null description: Abandon stack. name: stacks:abandon operations: - method: DELETE path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/abandon scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:create deprecated_since: null description: Create stack. name: stacks:create operations: - method: POST path: /v1/{tenant_id}/stacks scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:delete deprecated_since: null description: Delete stack. name: stacks:delete operations: - method: DELETE path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:detail deprecated_since: null description: List stacks in detail. name: stacks:detail operations: - method: GET path: /v1/{tenant_id}/stacks scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:export deprecated_since: null description: Export stack. name: stacks:export operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/export scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:generate_template deprecated_since: null description: Generate stack template. name: stacks:generate_template operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/template scope_types: - project - check_str: rule:deny_everybody description: List stacks globally. name: stacks:global_index operations: - method: GET path: /v1/{tenant_id}/stacks scope_types: null - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:index deprecated_since: null description: List stacks. name: stacks:index operations: - method: GET path: /v1/{tenant_id}/stacks scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:list_resource_types deprecated_since: null description: List resource types. name: stacks:list_resource_types operations: - method: GET path: /v1/{tenant_id}/resource_types scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:list_template_versions deprecated_since: null description: List template versions. name: stacks:list_template_versions operations: - method: GET path: /v1/{tenant_id}/template_versions scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:list_template_functions deprecated_since: null description: List template functions. name: stacks:list_template_functions operations: - method: GET path: /v1/{tenant_id}/template_versions/{template_version}/functions scope_types: - project - check_str: (role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s) deprecated_reason: null deprecated_rule: check_str: rule:allow_everybody name: stacks:lookup deprecated_since: null description: Find stack. name: stacks:lookup operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_identity} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:preview deprecated_since: null description: Preview stack. name: stacks:preview operations: - method: POST path: /v1/{tenant_id}/stacks/preview scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:resource_schema deprecated_since: null description: Show resource type schema. name: stacks:resource_schema operations: - method: GET path: /v1/{tenant_id}/resource_types/{type_name} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:show deprecated_since: null description: Show stack. name: stacks:show operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_identity} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:template deprecated_since: null description: Get stack template. name: stacks:template operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/template scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:environment deprecated_since: null description: Get stack environment. name: stacks:environment operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/environment scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:files deprecated_since: null description: Get stack files. name: stacks:files operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/files scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:update deprecated_since: null description: Update stack. name: stacks:update operations: - method: PUT path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:update_patch deprecated_since: null description: Update stack (PATCH). name: stacks:update_patch operations: - method: PATCH path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id} scope_types: - project - check_str: rule:stacks:update_patch description: Update stack (PATCH) with no changes. name: stacks:update_no_change operations: - method: PATCH path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:preview_update deprecated_since: null description: Preview update stack. name: stacks:preview_update operations: - method: PUT path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/preview scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:preview_update_patch deprecated_since: null description: Preview update stack (PATCH). name: stacks:preview_update_patch operations: - method: PATCH path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/preview scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:validate_template deprecated_since: null description: Validate template. name: stacks:validate_template operations: - method: POST path: /v1/{tenant_id}/validate scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:snapshot deprecated_since: null description: Snapshot Stack. name: stacks:snapshot operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:show_snapshot deprecated_since: null description: Show snapshot. name: stacks:show_snapshot operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots/{snapshot_id} scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:delete_snapshot deprecated_since: null description: Delete snapshot. name: stacks:delete_snapshot operations: - method: DELETE path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots/{snapshot_id} scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:list_snapshots deprecated_since: null description: List snapshots. name: stacks:list_snapshots operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots scope_types: - project - check_str: role:member and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:restore_snapshot deprecated_since: null description: Restore snapshot. name: stacks:restore_snapshot operations: - method: POST path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots/{snapshot_id}/restore scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:list_outputs deprecated_since: null description: List outputs. name: stacks:list_outputs operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/outputs scope_types: - project - check_str: role:reader and project_id:%(project_id)s deprecated_reason: null deprecated_rule: check_str: rule:deny_stack_user name: stacks:show_output deprecated_since: null description: Show outputs. name: stacks:show_output operations: - method: GET path: /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/outputs/{output_key} scope_types: - project ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4854963 heat_dashboard-16.0.0/heat_dashboard/conf/heat_policy.yaml0000664000175000017500000007460215257164522022433 0ustar00zuulzuul# Decides what is required for the 'is_admin:True' check to succeed. #"context_is_admin": "(role:admin and is_admin_project:True) OR (role:admin and system_scope:all)" # Default rule for project admin. #"project_admin": "role:admin" # Default rule for deny stack user. #"deny_stack_user": "not role:heat_stack_user" # Default rule for deny everybody. #"deny_everybody": "!" # Default rule for allow everybody. #"allow_everybody": "" # Performs non-lifecycle operations on the stack (Snapshot, Resume, # Cancel update, or check stack resources). This is the default for # all actions but can be overridden by more specific policies for # individual actions. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:action": "role:member and project_id:%(project_id)s" # DEPRECATED # "actions:action":"rule:deny_stack_user" has been deprecated since W # in favor of "actions:action":"role:member and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Create stack snapshot # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:snapshot": "role:member and project_id:%(project_id)s" # DEPRECATED # "actions:snapshot":"rule:deny_stack_user" has been deprecated since # W in favor of "actions:snapshot":"role:member and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Suspend a stack. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:suspend": "role:member and project_id:%(project_id)s" # DEPRECATED # "actions:suspend":"rule:deny_stack_user" has been deprecated since W # in favor of "actions:suspend":"role:member and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Resume a suspended stack. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:resume": "role:member and project_id:%(project_id)s" # DEPRECATED # "actions:resume":"rule:deny_stack_user" has been deprecated since W # in favor of "actions:resume":"role:member and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Check stack resources. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:check": "role:reader and project_id:%(project_id)s" # DEPRECATED # "actions:check":"rule:deny_stack_user" has been deprecated since W # in favor of "actions:check":"role:reader and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Cancel stack operation and roll back. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:cancel_update": "role:member and project_id:%(project_id)s" # DEPRECATED # "actions:cancel_update":"rule:deny_stack_user" has been deprecated # since W in favor of "actions:cancel_update":"role:member and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Cancel stack operation without rolling back. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/actions # Intended scope(s): project #"actions:cancel_without_rollback": "role:member and project_id:%(project_id)s" # DEPRECATED # "actions:cancel_without_rollback":"rule:deny_stack_user" has been # deprecated since W in favor of # "actions:cancel_without_rollback":"role:member and # project_id:%(project_id)s". # The actions API now supports system scope and default roles. # Show build information. # GET /v1/{tenant_id}/build_info # Intended scope(s): project #"build_info:build_info": "role:reader and project_id:%(project_id)s" # DEPRECATED # "build_info:build_info":"rule:deny_stack_user" has been deprecated # since W in favor of "build_info:build_info":"role:reader and # project_id:%(project_id)s". # The build API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:ListStacks": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:ListStacks":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:ListStacks":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:CreateStack": "role:member and project_id:%(project_id)s" # DEPRECATED # "cloudformation:CreateStack":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:CreateStack":"role:member and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:DescribeStacks": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:DescribeStacks":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:DescribeStacks":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:DeleteStack": "role:member and project_id:%(project_id)s" # DEPRECATED # "cloudformation:DeleteStack":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:DeleteStack":"role:member and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:UpdateStack": "role:member and project_id:%(project_id)s" # DEPRECATED # "cloudformation:UpdateStack":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:UpdateStack":"role:member and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:CancelUpdateStack": "role:member and project_id:%(project_id)s" # DEPRECATED # "cloudformation:CancelUpdateStack":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:CancelUpdateStack":"role:member and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:DescribeStackEvents": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:DescribeStackEvents":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:DescribeStackEvents":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:ValidateTemplate": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:ValidateTemplate":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:ValidateTemplate":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:GetTemplate": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:GetTemplate":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:GetTemplate":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:EstimateTemplateCost": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:EstimateTemplateCost":"rule:deny_stack_user" has # been deprecated since W in favor of # "cloudformation:EstimateTemplateCost":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:DescribeStackResource": "(role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s)" # DEPRECATED # "cloudformation:DescribeStackResource":"rule:allow_everybody" has # been deprecated since W in favor of # "cloudformation:DescribeStackResource":"(role:reader and # project_id:%(project_id)s) or (role:heat_stack_user and # project_id:%(project_id)s)". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:DescribeStackResources": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:DescribeStackResources":"rule:deny_stack_user" has # been deprecated since W in favor of # "cloudformation:DescribeStackResources":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # Intended scope(s): project #"cloudformation:ListStackResources": "role:reader and project_id:%(project_id)s" # DEPRECATED # "cloudformation:ListStackResources":"rule:deny_stack_user" has been # deprecated since W in favor of # "cloudformation:ListStackResources":"role:reader and # project_id:%(project_id)s". # The cloud formation API now supports system scope and default roles. # List events. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/events # Intended scope(s): project #"events:index": "role:reader and project_id:%(project_id)s" # DEPRECATED # "events:index":"rule:deny_stack_user" has been deprecated since W in # favor of "events:index":"role:reader and project_id:%(project_id)s". # The events API now supports system scope and default roles. # Show event. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/events/{event_id} # Intended scope(s): project #"events:show": "role:reader and project_id:%(project_id)s" # DEPRECATED # "events:show":"rule:deny_stack_user" has been deprecated since W in # favor of "events:show":"role:reader and project_id:%(project_id)s". # The events API now supports system scope and default roles. # List resources. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources # Intended scope(s): project #"resource:index": "role:reader and project_id:%(project_id)s" # DEPRECATED # "resource:index":"rule:deny_stack_user" has been deprecated since W # in favor of "resource:index":"role:reader and # project_id:%(project_id)s". # The resources API now supports system scope and default roles. # Show resource metadata. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/metadata # Intended scope(s): project #"resource:metadata": "(role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s)" # DEPRECATED # "resource:metadata":"rule:allow_everybody" has been deprecated since # W in favor of "resource:metadata":"(role:reader and # project_id:%(project_id)s) or (role:heat_stack_user and # project_id:%(project_id)s)". # The resources API now supports system scope and default roles. # Signal resource. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/signal # Intended scope(s): project #"resource:signal": "(role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s)" # DEPRECATED # "resource:signal":"rule:allow_everybody" has been deprecated since W # in favor of "resource:signal":"(role:reader and # project_id:%(project_id)s) or (role:heat_stack_user and # project_id:%(project_id)s)". # The resources API now supports system scope and default roles. # Mark resource as unhealthy. # PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name_or_physical_id} # Intended scope(s): project #"resource:mark_unhealthy": "role:member and project_id:%(project_id)s" # DEPRECATED # "resource:mark_unhealthy":"rule:deny_stack_user" has been deprecated # since W in favor of "resource:mark_unhealthy":"role:member and # project_id:%(project_id)s". # The resources API now supports system scope and default roles. # Show resource. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name} # Intended scope(s): project #"resource:show": "role:reader and project_id:%(project_id)s" # DEPRECATED # "resource:show":"rule:deny_stack_user" has been deprecated since W # in favor of "resource:show":"role:reader and # project_id:%(project_id)s". # The resources API now supports system scope and default roles. # Intended scope(s): project #"resource_types:OS::Nova::Flavor": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Cinder::EncryptedVolumeType": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Cinder::VolumeType": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Cinder::Quota": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::Quota": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Nova::Quota": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Octavia::Quota": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Manila::ShareType": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::ProviderNet": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::QoSPolicy": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::QoSBandwidthLimitRule": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::QoSDscpMarkingRule": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::QoSMinimumBandwidthRule": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::QoSMinimumPacketRateRule": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Neutron::Segment": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Nova::HostAggregate": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Cinder::QoSSpecs": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Cinder::QoSAssociation": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Keystone::*": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Blazar::Host": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Octavia::Flavor": "rule:project_admin" # Intended scope(s): project #"resource_types:OS::Octavia::FlavorProfile": "rule:project_admin" # Intended scope(s): project #"service:index": "role:admin and project_id:%(project_id)s" # DEPRECATED # "service:index":"rule:context_is_admin" has been deprecated since W # in favor of "service:index":"role:admin and # project_id:%(project_id)s". # The service API now supports system scope and default roles. # List configs globally. # GET /v1/{tenant_id}/software_configs #"software_configs:global_index": "rule:deny_everybody" # List configs. # GET /v1/{tenant_id}/software_configs # Intended scope(s): project #"software_configs:index": "role:reader and project_id:%(project_id)s" # DEPRECATED # "software_configs:index":"rule:deny_stack_user" has been deprecated # since W in favor of "software_configs:index":"role:reader and # project_id:%(project_id)s". # The software configuration API now support system scope and default # roles. # Create config. # POST /v1/{tenant_id}/software_configs # Intended scope(s): project #"software_configs:create": "role:member and project_id:%(project_id)s" # DEPRECATED # "software_configs:create":"rule:deny_stack_user" has been deprecated # since W in favor of "software_configs:create":"role:member and # project_id:%(project_id)s". # The software configuration API now support system scope and default # roles. # Show config details. # GET /v1/{tenant_id}/software_configs/{config_id} # Intended scope(s): project #"software_configs:show": "role:reader and project_id:%(project_id)s" # DEPRECATED # "software_configs:show":"rule:deny_stack_user" has been deprecated # since W in favor of "software_configs:show":"role:reader and # project_id:%(project_id)s". # The software configuration API now support system scope and default # roles. # Delete config. # DELETE /v1/{tenant_id}/software_configs/{config_id} # Intended scope(s): project #"software_configs:delete": "role:member and project_id:%(project_id)s" # DEPRECATED # "software_configs:delete":"rule:deny_stack_user" has been deprecated # since W in favor of "software_configs:delete":"role:member and # project_id:%(project_id)s". # The software configuration API now support system scope and default # roles. # List deployments. # GET /v1/{tenant_id}/software_deployments # Intended scope(s): project #"software_deployments:index": "role:reader and project_id:%(project_id)s" # DEPRECATED # "software_deployments:index":"rule:deny_stack_user" has been # deprecated since W in favor of # "software_deployments:index":"role:reader and # project_id:%(project_id)s". # The software deployment API now supports system scope and default # roles. # Create deployment. # POST /v1/{tenant_id}/software_deployments # Intended scope(s): project #"software_deployments:create": "role:member and project_id:%(project_id)s" # DEPRECATED # "software_deployments:create":"rule:deny_stack_user" has been # deprecated since W in favor of # "software_deployments:create":"role:member and # project_id:%(project_id)s". # The software deployment API now supports system scope and default # roles. # Show deployment details. # GET /v1/{tenant_id}/software_deployments/{deployment_id} # Intended scope(s): project #"software_deployments:show": "role:reader and project_id:%(project_id)s" # DEPRECATED # "software_deployments:show":"rule:deny_stack_user" has been # deprecated since W in favor of # "software_deployments:show":"role:reader and # project_id:%(project_id)s". # The software deployment API now supports system scope and default # roles. # Update deployment. # PUT /v1/{tenant_id}/software_deployments/{deployment_id} # Intended scope(s): project #"software_deployments:update": "role:member and project_id:%(project_id)s" # DEPRECATED # "software_deployments:update":"rule:deny_stack_user" has been # deprecated since W in favor of # "software_deployments:update":"role:member and # project_id:%(project_id)s". # The software deployment API now supports system scope and default # roles. # Delete deployment. # DELETE /v1/{tenant_id}/software_deployments/{deployment_id} # Intended scope(s): project #"software_deployments:delete": "role:member and project_id:%(project_id)s" # DEPRECATED # "software_deployments:delete":"rule:deny_stack_user" has been # deprecated since W in favor of # "software_deployments:delete":"role:member and # project_id:%(project_id)s". # The software deployment API now supports system scope and default # roles. # Show server configuration metadata. # GET /v1/{tenant_id}/software_deployments/metadata/{server_id} # Intended scope(s): project #"software_deployments:metadata": "(role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s)" # Abandon stack. # DELETE /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/abandon # Intended scope(s): project #"stacks:abandon": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:abandon":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:abandon":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Create stack. # POST /v1/{tenant_id}/stacks # Intended scope(s): project #"stacks:create": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:create":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:create":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Delete stack. # DELETE /v1/{tenant_id}/stacks/{stack_name}/{stack_id} # Intended scope(s): project #"stacks:delete": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:delete":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:delete":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List stacks in detail. # GET /v1/{tenant_id}/stacks # Intended scope(s): project #"stacks:detail": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:detail":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:detail":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Export stack. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/export # Intended scope(s): project #"stacks:export": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:export":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:export":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Generate stack template. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/template # Intended scope(s): project #"stacks:generate_template": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:generate_template":"rule:deny_stack_user" has been # deprecated since W in favor of # "stacks:generate_template":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List stacks globally. # GET /v1/{tenant_id}/stacks #"stacks:global_index": "rule:deny_everybody" # List stacks. # GET /v1/{tenant_id}/stacks # Intended scope(s): project #"stacks:index": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:index":"rule:deny_stack_user" has been deprecated since W in # favor of "stacks:index":"role:reader and project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List resource types. # GET /v1/{tenant_id}/resource_types # Intended scope(s): project #"stacks:list_resource_types": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:list_resource_types":"rule:deny_stack_user" has been # deprecated since W in favor of # "stacks:list_resource_types":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List template versions. # GET /v1/{tenant_id}/template_versions # Intended scope(s): project #"stacks:list_template_versions": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:list_template_versions":"rule:deny_stack_user" has been # deprecated since W in favor of # "stacks:list_template_versions":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List template functions. # GET /v1/{tenant_id}/template_versions/{template_version}/functions # Intended scope(s): project #"stacks:list_template_functions": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:list_template_functions":"rule:deny_stack_user" has been # deprecated since W in favor of # "stacks:list_template_functions":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Find stack. # GET /v1/{tenant_id}/stacks/{stack_identity} # Intended scope(s): project #"stacks:lookup": "(role:reader and project_id:%(project_id)s) or (role:heat_stack_user and project_id:%(project_id)s)" # DEPRECATED # "stacks:lookup":"rule:allow_everybody" has been deprecated since W # in favor of "stacks:lookup":"(role:reader and # project_id:%(project_id)s) or (role:heat_stack_user and # project_id:%(project_id)s)". # The stack API now supports system scope and default roles. # Preview stack. # POST /v1/{tenant_id}/stacks/preview # Intended scope(s): project #"stacks:preview": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:preview":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:preview":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Show resource type schema. # GET /v1/{tenant_id}/resource_types/{type_name} # Intended scope(s): project #"stacks:resource_schema": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:resource_schema":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:resource_schema":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Show stack. # GET /v1/{tenant_id}/stacks/{stack_identity} # Intended scope(s): project #"stacks:show": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:show":"rule:deny_stack_user" has been deprecated since W in # favor of "stacks:show":"role:reader and project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Get stack template. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/template # Intended scope(s): project #"stacks:template": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:template":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:template":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Get stack environment. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/environment # Intended scope(s): project #"stacks:environment": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:environment":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:environment":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Get stack files. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/files # Intended scope(s): project #"stacks:files": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:files":"rule:deny_stack_user" has been deprecated since W in # favor of "stacks:files":"role:reader and project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Update stack. # PUT /v1/{tenant_id}/stacks/{stack_name}/{stack_id} # Intended scope(s): project #"stacks:update": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:update":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:update":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Update stack (PATCH). # PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id} # Intended scope(s): project #"stacks:update_patch": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:update_patch":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:update_patch":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Update stack (PATCH) with no changes. # PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id} # Intended scope(s): project #"stacks:update_no_change": "rule:stacks:update_patch" # Preview update stack. # PUT /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/preview # Intended scope(s): project #"stacks:preview_update": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:preview_update":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:preview_update":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Preview update stack (PATCH). # PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/preview # Intended scope(s): project #"stacks:preview_update_patch": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:preview_update_patch":"rule:deny_stack_user" has been # deprecated since W in favor of # "stacks:preview_update_patch":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Validate template. # POST /v1/{tenant_id}/validate # Intended scope(s): project #"stacks:validate_template": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:validate_template":"rule:deny_stack_user" has been # deprecated since W in favor of # "stacks:validate_template":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Snapshot Stack. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots # Intended scope(s): project #"stacks:snapshot": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:snapshot":"rule:deny_stack_user" has been deprecated since W # in favor of "stacks:snapshot":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Show snapshot. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots/{snapshot_id} # Intended scope(s): project #"stacks:show_snapshot": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:show_snapshot":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:show_snapshot":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Delete snapshot. # DELETE /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots/{snapshot_id} # Intended scope(s): project #"stacks:delete_snapshot": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:delete_snapshot":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:delete_snapshot":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List snapshots. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots # Intended scope(s): project #"stacks:list_snapshots": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:list_snapshots":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:list_snapshots":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Restore snapshot. # POST /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/snapshots/{snapshot_id}/restore # Intended scope(s): project #"stacks:restore_snapshot": "role:member and project_id:%(project_id)s" # DEPRECATED # "stacks:restore_snapshot":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:restore_snapshot":"role:member and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # List outputs. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/outputs # Intended scope(s): project #"stacks:list_outputs": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:list_outputs":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:list_outputs":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. # Show outputs. # GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/outputs/{output_key} # Intended scope(s): project #"stacks:show_output": "role:reader and project_id:%(project_id)s" # DEPRECATED # "stacks:show_output":"rule:deny_stack_user" has been deprecated # since W in favor of "stacks:show_output":"role:reader and # project_id:%(project_id)s". # The stack API now supports system scope and default roles. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2962623 heat_dashboard-16.0.0/heat_dashboard/content/0000775000175000017500000000000015257164606017766 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/__init__.py0000664000175000017500000000000015257164522022062 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2982621 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/0000775000175000017500000000000015257164606023041 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/__init__.py0000664000175000017500000000000015257164522025135 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/panel.py0000664000175000017500000000151015257164522024504 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ import horizon class ResourceTypes(horizon.Panel): name = _("Resource Types") slug = "resource_types" permissions = ('openstack.services.orchestration',) policy_rules = (("orchestration", "stacks:list_resource_types"),) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/tables.py0000664000175000017500000000233715257164522024667 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ from horizon import tables class ResourceTypesFilterAction(tables.FilterAction): filter_type = 'server' filter_choices = (('name', _('Type ='), True, _("Case sensitive")),) class ResourceTypesTable(tables.DataTable): name = tables.Column("resource_type", verbose_name=_("Type"), link="horizon:project:resource_types:details",) def get_object_id(self, resource): return resource.resource_type class Meta: name = "resource_types" verbose_name = _("Resource Types") table_actions = (ResourceTypesFilterAction,) multi_select = False ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/tabs.py0000664000175000017500000000221215257164522024336 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ from horizon import tabs class ResourceTypeOverviewTab(tabs.Tab): name = _("Overview") slug = "resource_type_overview" template_name = "project/resource_types/_details.html" def get_context_data(self, request): return {"r_type": self.tab_group.kwargs['rt'], "r_type_attributes": self.tab_group.kwargs['rt_attributes'], "r_type_properties": self.tab_group.kwargs['rt_properties']} class ResourceTypeDetailsTabs(tabs.TabGroup): slug = "resource_type_details" tabs = (ResourceTypeOverviewTab,) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.257263 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/templates/0000775000175000017500000000000015257164606025037 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2982621 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/templates/resource_types/0000775000175000017500000000000015257164606030112 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/templates/resource_types/_details.html0000664000175000017500000000036015257164522032560 0ustar00zuulzuul{% load i18n %}
{{ r_type }}

{% trans "Attributes" %}

{{ r_type_attributes }}
  

{% trans "Properties" %}

{{ r_type_properties }}
  
././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.299262 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/templates/stacks.resource_types/0000775000175000017500000000000015257164606031401 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000021700000000000010215 xustar00115 path=heat_dashboard-16.0.0/heat_dashboard/content/resource_types/templates/stacks.resource_types/_details.html 28 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/templates/stacks.resource_types/_details0000664000175000017500000000036015257164522033104 0ustar00zuulzuul{% load i18n %}
{{ r_type }}

{% trans "Attributes" %}

{{ r_type_attributes }}
  

{% trans "Properties" %}

{{ r_type_properties }}
  
././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/urls.py0000664000175000017500000000147015257164522024377 0ustar00zuulzuul# 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 django.urls import re_path from heat_dashboard.content.resource_types import views urlpatterns = [ re_path(r'^$', views.ResourceTypesView.as_view(), name='index'), re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='details'), ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/resource_types/views.py0000664000175000017500000000546215257164522024554 0ustar00zuulzuul# 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. import yaml from django.urls import reverse from django.utils.translation import gettext_lazy as _ from horizon import exceptions from horizon import tables from horizon import tabs from heat_dashboard import api import heat_dashboard.content.resource_types.tables as project_tables import heat_dashboard.content.resource_types.tabs as project_tabs class ResourceTypesView(tables.DataTableView): table_class = project_tables.ResourceTypesTable page_title = _("Resource Types") def get_data(self): try: filters = self.get_filters() if 'name' in filters: filters['name'] = '.*' + filters['name'] r_types = sorted(api.heat.resource_types_list(self.request, filters=filters), key=lambda resource: resource.resource_type) except Exception: r_types = [] msg = _('Unable to retrieve stack resource types.') exceptions.handle(self.request, msg) return r_types class DetailView(tabs.TabView): tab_group_class = project_tabs.ResourceTypeDetailsTabs template_name = 'horizon/common/_detail.html' page_title = "{{ resource_type }}" def get_resource_type(self, request, **kwargs): try: resource_type_overview = api.heat.resource_type_get( request, kwargs['resource_type']) return resource_type_overview except Exception: msg = _('Unable to retrieve resource type details.') exceptions.handle(request, msg, redirect=self.get_redirect_url()) def get_tabs(self, request, **kwargs): resource_type_overview = self.get_resource_type(request, **kwargs) r_type = resource_type_overview['resource_type'] r_type_attributes = resource_type_overview['attributes'] r_type_properties = resource_type_overview['properties'] return self.tab_group_class( request, rt=r_type, rt_attributes=yaml.safe_dump(r_type_attributes, indent=2), rt_properties=yaml.safe_dump(r_type_properties, indent=2), **kwargs) @staticmethod def get_redirect_url(): return reverse('horizon:project:resource_types:index') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3012621 heat_dashboard-16.0.0/heat_dashboard/content/stacks/0000775000175000017500000000000015257164606021256 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4864962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/__init__.py0000664000175000017500000000000015257164522023352 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4874961 heat_dashboard-16.0.0/heat_dashboard/content/stacks/api.py0000664000175000017500000000553115257164522022402 0ustar00zuulzuul# 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. import json from heatclient.v1 import stacks from heat_dashboard.api import heat from heat_dashboard.content.stacks import mappings from heat_dashboard.content.stacks import sro def d3_data(request, stack_id=''): try: stack = heat.stack_get(request, stack_id) except Exception: stack_data = { 'id': stack_id, 'stack_name': request.session.get('stack_name', ''), 'stack_status': 'DELETE_COMPLETE', 'stack_status_reason': 'DELETE_COMPLETE', } stack = stacks.Stack(stacks.StackManager(None), stack_data) try: resources = heat.resources_list(request, stack.stack_name) except Exception: resources = [] d3_data = {"nodes": [], "stack": {}} if stack: stack_image = mappings.get_resource_image(stack.stack_status, 'stack') stack_node = { 'stack_id': stack.id, 'name': stack.stack_name, 'status': stack.stack_status, 'image': stack_image, 'image_size': 60, 'image_x': -30, 'image_y': -30, 'text_x': 40, 'text_y': ".35em", 'in_progress': (stack.status == 'IN_PROGRESS'), 'info_box': sro.stack_info(stack, stack_image) } d3_data['stack'] = stack_node if resources: for resource in resources: resource_image = mappings.get_resource_image( resource.resource_status, resource.resource_type) resource_status = mappings.get_resource_status( resource.resource_status) if resource_status in ('IN_PROGRESS', 'INIT'): in_progress = True else: in_progress = False resource_node = { 'name': resource.resource_name, 'status': resource.resource_status, 'image': resource_image, 'required_by': resource.required_by, 'image_size': 50, 'image_x': -25, 'image_y': -25, 'text_x': 35, 'text_y': ".35em", 'in_progress': in_progress, 'info_box': sro.resource_info(resource) } d3_data['nodes'].append(resource_node) return json.dumps(d3_data) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6614952 heat_dashboard-16.0.0/heat_dashboard/content/stacks/forms.py0000664000175000017500000004701615257164523022764 0ustar00zuulzuul# 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. import json import logging from django.conf import settings from django.template.defaultfilters import filesizeformat from django.utils import html from django.utils.translation import gettext_lazy as _ from django.views.decorators.debug import sensitive_variables from oslo_utils import strutils from horizon import exceptions from horizon import forms from horizon import messages from heat_dashboard import api from openstack_dashboard.dashboards.project.images \ import utils as image_utils from openstack_dashboard.dashboards.project.instances \ import utils as instance_utils LOG = logging.getLogger(__name__) def image_field_data(request, include_empty_option=False): """Returns a list of tuples of all images. Generates a sorted list of images available. And returns a list of (id, name) tuples. :param request: django http request object :param include_empty_option: flag to include a empty tuple in the front of the list :return: list of (id, name) tuples """ try: images = image_utils.get_available_images( request, request.user.project_id) except Exception: exceptions.handle(request, _('Unable to retrieve images')) images.sort(key=lambda c: c.name) images_list = [] for image in images: image_label = "{} ({})".format(image.name, filesizeformat(image.size)) images_list.append((image.id, image_label)) if not images: return [("", _("No images available")), ] if include_empty_option: return [("", _("Select Image")), ] + images_list return images_list def create_upload_form_attributes(prefix, input_type, name): """Creates attribute dicts for the switchable upload form :type prefix: str :param prefix: prefix (environment, template) of field :type input_type: str :param input_type: field type (file, raw, url) :type name: str :param name: translated text label to display to user :rtype: dict :return: an attribute set to pass to form build """ attributes = {'class': 'switched', 'data-switch-on': prefix + 'source'} attributes['data-' + prefix + 'source-' + input_type] = name return attributes class TemplateForm(forms.SelfHandlingForm): class Meta: name = _('Select Template') help_text = _('Select a template to launch a stack.') # TODO(jomara) - update URL choice for template & environment files # w/ client side download when applicable base_choices = [('file', _('File')), ('raw', _('Direct Input'))] url_choice = [('url', _('URL'))] attributes = {'class': 'switchable', 'data-slug': 'templatesource'} template_source = forms.ChoiceField(label=_('Template Source'), choices=base_choices + url_choice, widget=forms.ThemableSelectWidget( attrs=attributes)) attributes = create_upload_form_attributes( 'template', 'file', _('Template File')) template_upload = forms.FileField( label=_('Template File'), help_text=_('A local template to upload.'), widget=forms.FileInput(attrs=attributes), required=False) attributes = create_upload_form_attributes( 'template', 'url', _('Template URL')) template_url = forms.URLField( label=_('Template URL'), help_text=_('An external (HTTP) URL to load the template from.'), widget=forms.TextInput(attrs=attributes), required=False) attributes = create_upload_form_attributes( 'template', 'raw', _('Template Data')) template_data = forms.CharField( label=_('Template Data'), help_text=_('The raw contents of the template.'), widget=forms.widgets.Textarea(attrs=attributes), required=False, strip=False) attributes = {'data-slug': 'envsource', 'class': 'switchable'} environment_source = forms.ChoiceField( label=_('Environment Source'), choices=base_choices, widget=forms.ThemableSelectWidget(attrs=attributes), required=False) attributes = create_upload_form_attributes( 'env', 'file', _('Environment File')) environment_upload = forms.FileField( label=_('Environment File'), help_text=_('A local environment to upload.'), widget=forms.FileInput(attrs=attributes), required=False) attributes = create_upload_form_attributes( 'env', 'raw', _('Environment Data')) environment_data = forms.CharField( label=_('Environment Data'), help_text=_('The raw contents of the environment file.'), widget=forms.widgets.Textarea(attrs=attributes), required=False, strip=False) referenced_files = forms.CharField(label=_('Referenced Files'), widget=forms.widgets.HiddenInput, required=False) def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super().__init__(*args, **kwargs) def clean(self): cleaned = super().clean() files = self.request.FILES self.clean_uploaded_files('template', _('template'), cleaned, files) self.clean_uploaded_files('environment', _('environment'), cleaned, files) referenced_files = cleaned.get('referenced_files') if referenced_files: referenced_files = json.loads(referenced_files) elif referenced_files == '': referenced_files = {} # Validate the template and get back the params. kwargs = {} if cleaned['environment_data']: kwargs['environment'] = cleaned['environment_data'] try: files, tpl =\ api.heat.get_template_files(cleaned.get('template_data'), cleaned.get('template_url'), referenced_files) kwargs['files'] = files kwargs['template'] = tpl validated = api.heat.template_validate(self.request, **kwargs) cleaned['template_validate'] = validated cleaned['template_validate']['files'] = files cleaned['template_validate']['template'] = tpl except Exception as e: raise forms.ValidationError(str(e)) return cleaned def clean_uploaded_files(self, prefix, field_label, cleaned, files): """Cleans Template & Environment data from form upload. Does some of the crunchy bits for processing uploads vs raw data depending on what the user specified. Identical process for environment data & template data. :type prefix: str :param prefix: prefix (environment, template) of field :type field_label: str :param field_label: translated prefix str for messages :type input_type: dict :param prefix: existing cleaned fields from form :rtype: dict :return: cleaned dict including environment & template data """ upload_str = prefix + "_upload" data_str = prefix + "_data" url = cleaned.get(prefix + '_url') data = cleaned.get(prefix + '_data') has_upload = upload_str in files # Uploaded file handler if has_upload and not url: log_template_name = files[upload_str].name LOG.info('got upload %s', log_template_name) try: tpl = files[upload_str].read().decode('utf-8') if tpl.startswith('{'): json.loads(tpl) cleaned[data_str] = tpl except Exception as e: msg = _('There was a problem parsing the' ' %(prefix)s: %(error)s') msg = msg % {'prefix': prefix, 'error': str(e)} raise forms.ValidationError(msg) # URL handler elif url and (has_upload or data): msg = _('Please specify a %s using only one source method.') msg = msg % field_label raise forms.ValidationError(msg) elif prefix == 'template': # Check for raw template input - blank environment allowed if not url and not data: msg = _('You must specify a template via one of the ' 'available sources.') raise forms.ValidationError(msg) def create_kwargs(self, data): kwargs = {'parameters': data['template_validate'], 'environment_data': data['environment_data']} if data.get('stack_id'): kwargs['stack_id'] = data['stack_id'] return kwargs def handle(self, request, data): kwargs = self.create_kwargs(data) # NOTE (gabriel): This is a bit of a hack, essentially rewriting this # request so that we can chain it as an input to the next view... # but hey, it totally works. request.method = 'GET' return self.next_view.as_view()(request, **kwargs) class ChangeTemplateForm(TemplateForm): class Meta: name = _('Edit Template') help_text = _('Select a new template to re-launch a stack.') stack_id = forms.CharField(label=_('Stack ID'), widget=forms.widgets.HiddenInput) stack_name = forms.CharField(label=_('Stack Name'), widget=forms.TextInput(attrs={'readonly': 'readonly'})) class PreviewTemplateForm(TemplateForm): class Meta: name = _('Preview Template') help_text = _('Select a new template to preview a stack.') class CreateStackForm(forms.SelfHandlingForm): param_prefix = '__param_' class Meta: name = _('Create Stack') environment_data = forms.CharField( widget=forms.widgets.HiddenInput, required=False, strip=False) parameters = forms.CharField( widget=forms.widgets.HiddenInput) stack_name = forms.RegexField( max_length=255, label=_('Stack Name'), help_text=_('Name of the stack to create.'), regex=r"^[a-zA-Z][a-zA-Z0-9_.-]*$", error_messages={'invalid': _('Name must start with a letter and may ' 'only contain letters, numbers, underscores, ' 'periods and hyphens.')}) timeout_mins = forms.IntegerField( initial=60, label=_('Creation Timeout (minutes)'), help_text=_('Stack creation timeout in minutes.')) enable_rollback = forms.BooleanField( label=_('Rollback On Failure'), help_text=_('Enable rollback on create/update failure.'), required=False) def __init__(self, *args, **kwargs): parameters = kwargs.pop('parameters') # special case: load template data from API, not passed in params if kwargs.get('validate_me'): parameters = kwargs.pop('validate_me') super().__init__(*args, **kwargs) if self._stack_password_enabled(): self.fields['password'] = forms.CharField( label=_('Password for user "%s"') % self.request.user.username, help_text=_('This is required for operations to be performed ' 'throughout the lifecycle of the stack'), widget=forms.PasswordInput()) self._build_parameter_fields(parameters) def _stack_password_enabled(self): stack_settings = getattr(settings, 'OPENSTACK_HEAT_STACK', {}) return stack_settings.get('enable_user_pass', True) def _build_parameter_fields(self, template_validate): self.help_text = template_validate['Description'] params = template_validate.get('Parameters', {}) if template_validate.get('ParameterGroups'): params_in_order = [] for group in template_validate['ParameterGroups']: for param in group.get('parameters', []): if param in params: params_in_order.append((param, params[param])) else: # no parameter groups, simply sorted to make the order fixed params_in_order = sorted(params.items()) for param_key, param in params_in_order: field = None field_key = self.param_prefix + param_key initial = param.get('Value', param.get('DefaultValue', param.get('Default'))) field_args = { 'initial': initial, 'label': param.get('Label', param_key), 'help_text': html.escape(param.get('Description', '')), 'required': initial is None, } param_type = param.get('Type', None) hidden = strutils.bool_from_string(param.get('NoEcho', 'false')) if 'CustomConstraint' in param: choices = self._populate_custom_choices( param['CustomConstraint']) field_args['choices'] = choices field = forms.ChoiceField(**field_args) elif 'AllowedValues' in param: choices = map(lambda x: (x, x), param['AllowedValues']) field_args['choices'] = choices field = forms.ChoiceField(**field_args) elif param_type == 'Json' and 'Default' in param: field_args['initial'] = json.dumps(param['Default']) field = forms.CharField(**field_args) elif param_type in ('CommaDelimitedList', 'String', 'Json'): if 'MinLength' in param: field_args['min_length'] = int(param['MinLength']) field_args['required'] = field_args['min_length'] > 0 if 'MaxLength' in param: field_args['max_length'] = int(param['MaxLength']) if hidden: field_args['widget'] = forms.PasswordInput( render_value=True) field = forms.CharField(**field_args) elif param_type == 'Number': if 'MinValue' in param: field_args['min_value'] = int(param['MinValue']) if 'MaxValue' in param: field_args['max_value'] = int(param['MaxValue']) field = forms.IntegerField(**field_args) elif param_type == 'Boolean': field_args['required'] = False field = forms.BooleanField(**field_args) if field: self.fields[field_key] = field @sensitive_variables('password') def handle(self, request, data): prefix_length = len(self.param_prefix) params_list = [(k[prefix_length:], v) for (k, v) in data.items() if k.startswith(self.param_prefix)] fields = { 'stack_name': data.get('stack_name'), 'timeout_mins': data.get('timeout_mins'), 'disable_rollback': not data.get('enable_rollback'), 'parameters': dict(params_list), 'files': json.loads(data.get('parameters')).get('files'), 'template': json.loads(data.get('parameters')).get('template') } if data.get('password'): fields['password'] = data.get('password') if data.get('environment_data'): fields['environment'] = data.get('environment_data') try: api.heat.stack_create(self.request, **fields) messages.info(request, _("Stack creation started.")) return True except Exception: exceptions.handle(request) def _populate_custom_choices(self, custom_type): if custom_type == 'neutron.network': return instance_utils.network_field_data(self.request, False) if custom_type == 'nova.keypair': return instance_utils.keypair_field_data(self.request, False) if custom_type == 'glance.image': return image_field_data(self.request, False) if custom_type == 'nova.flavor': return instance_utils.flavor_field_data(self.request, False) return [] class EditStackForm(CreateStackForm): class Meta: name = _('Update Stack Parameters') stack_id = forms.CharField( label=_('Stack ID'), widget=forms.widgets.HiddenInput) stack_name = forms.CharField( label=_('Stack Name'), widget=forms.TextInput(attrs={'readonly': 'readonly'})) timeout_mins = forms.IntegerField( initial=60, label=_('Updating Timeout (minutes)'), help_text=_('Stack updating timeout in minutes.')) @sensitive_variables('password') def handle(self, request, data): prefix_length = len(self.param_prefix) params_list = [(k[prefix_length:], v) for (k, v) in data.items() if k.startswith(self.param_prefix)] stack_id = data.get('stack_id') fields = { 'stack_name': data.get('stack_name'), 'timeout_mins': data.get('timeout_mins'), 'disable_rollback': not data.get('enable_rollback'), 'parameters': dict(params_list), 'files': json.loads(data.get('parameters')).get('files'), 'template': json.loads(data.get('parameters')).get('template') } if data.get('password'): fields['password'] = data.get('password') if data.get('environment_data'): fields['environment'] = data.get('environment_data') try: api.heat.stack_update(self.request, stack_id=stack_id, **fields) messages.info(request, _("Stack update started.")) return True except Exception: exceptions.handle(request) class PreviewStackForm(CreateStackForm): class Meta: name = _('Preview Stack Parameters') def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(CreateStackForm, self).__init__(*args, **kwargs) def handle(self, request, data): prefix_length = len(self.param_prefix) params_list = [(k[prefix_length:], v) for (k, v) in data.items() if k.startswith(self.param_prefix)] fields = { 'stack_name': data.get('stack_name'), 'timeout_mins': data.get('timeout_mins'), 'disable_rollback': not data.get('enable_rollback'), 'parameters': dict(params_list), 'files': json.loads(data.get('parameters')).get('files'), 'template': json.loads(data.get('parameters')).get('template') } if data.get('environment_data'): fields['environment'] = data.get('environment_data') try: stack_preview = api.heat.stack_preview(self.request, **fields) request.method = 'GET' return self.next_view.as_view()(request, stack_preview=stack_preview) except Exception: exceptions.handle(request) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6614952 heat_dashboard-16.0.0/heat_dashboard/content/stacks/mappings.py0000664000175000017500000003364515257164523023457 0ustar00zuulzuul# 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. import json import logging import urllib.parse as urlparse from django.conf import settings from django.template.defaultfilters import register from django.urls import reverse from django.utils import html from django.utils import safestring from openstack_dashboard.api import swift LOG = logging.getLogger(__name__) resource_urls = { "AWS::AutoScaling::AutoScalingGroup": { 'link': 'horizon:project:stacks:detail'}, "AWS::CloudFormation::Stack": { 'link': 'horizon:project:stacks:detail'}, "AWS::EC2::Instance": { 'link': 'horizon:project:instances:detail'}, "AWS::EC2::InternetGateway": { 'link': 'horizon:project:networks:ports:detail'}, "AWS::EC2::NetworkInterface": { 'link': 'horizon:project:networks:ports:detail'}, "AWS::EC2::RouteTable": { 'link': 'horizon:project:routers:detail'}, "AWS::EC2::SecurityGroup": { 'link': 'horizon:project:security_groups:index'}, "AWS::EC2::Subnet": { 'link': 'horizon:project:networks:subnets:detail'}, "AWS::EC2::Volume": { 'link': 'horizon:project:volumes:detail'}, "AWS::EC2::VPC": { 'link': 'horizon:project:networks:detail'}, "AWS::S3::Bucket": { 'link': 'horizon:project:containers:index'}, "OS::Cinder::Volume": { 'link': 'horizon:project:volumes:detail'}, "OS::Heat::AccessPolicy": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::AutoScalingGroup": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::CloudConfig": { 'link': 'horizon:project:stacks:detail'}, "OS::Neutron::Firewall": { 'link': 'horizon:project:firewalls:firewalldetails'}, "OS::Neutron::FirewallPolicy": { 'link': 'horizon:project:firewalls:policydetails'}, "OS::Neutron::FirewallRule": { 'link': 'horizon:project:firewalls:ruledetails'}, "OS::Heat::HARestarter": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::InstanceGroup": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::MultipartMime": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::ResourceGroup": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::SoftwareConfig": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::StructuredConfig": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::StructuredDeployment": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::Stack": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::WaitCondition": { 'link': 'horizon:project:stacks:detail'}, "OS::Heat::WaitConditionHandle": { 'link': 'horizon:project:stacks:detail'}, "OS::Neutron::IKEPolicy": { 'link': 'horizon:project:vpn:ikepolicydetails'}, "OS::Neutron::IPsecPolicy": { 'link': 'horizon:project:vpn:ipsecpolicydetails'}, "OS::Neutron::IPsecSiteConnection": { 'link': 'horizon:project:vpn:ipsecsiteconnectiondetails'}, "OS::Neutron::Net": { 'link': 'horizon:project:networks:detail'}, "OS::Neutron::Port": { 'link': 'horizon:project:networks:ports:detail'}, "OS::Neutron::Router": { 'link': 'horizon:project:routers:detail'}, "OS::Neutron::Subnet": { 'link': 'horizon:project:networks:subnets:detail'}, "OS::Neutron::VPNService": { 'link': 'horizon:project:vpn:vpnservicedetails'}, "OS::Nova::KeyPair": { 'link': 'horizon:project:key_pairs:index'}, "OS::Nova::Server": { 'link': 'horizon:project:instances:detail'}, "OS::Swift::Container": { 'link': 'horizon:project:containers:index', 'format_pattern': '%s' + swift.FOLDER_DELIMITER}, } def resource_to_url(resource): if (not resource or not resource.physical_resource_id or not hasattr(resource, 'resource_type')): return None mapping = resource_urls.get(resource.resource_type, {}) try: if 'link' not in mapping: return None format_pattern = mapping.get('format_pattern') or '%s' rid = format_pattern % resource.physical_resource_id url = reverse(mapping['link'], args=(rid,)) except Exception as e: LOG.exception(e) return None return url @register.filter def stack_output(output): if not output: return '' if isinstance(output, str): parts = urlparse.urlsplit(output) if parts.netloc and parts.scheme in ('http', 'https'): url = html.escape(output) safe_link = '{}'.format(url, url) return safestring.mark_safe(safe_link) if isinstance(output, dict) or isinstance(output, list): output = json.dumps(output, indent=2) return safestring.mark_safe('
%s
' % html.escape(output)) static_url = getattr(settings, "STATIC_URL", "/static/") prefix = 'dashboard/project/heat_dashboard/img/' resource_images = { 'LB_FAILED': static_url + prefix + 'lb-red.svg', 'LB_DELETE': static_url + prefix + 'lb-red.svg', 'LB_IN_PROGRESS': static_url + prefix + 'lb-gray.gif', 'LB_INIT': static_url + prefix + 'lb-gray.svg', 'LB_COMPLETE': static_url + prefix + 'lb-green.svg', 'DB_FAILED': static_url + prefix + 'db-red.svg', 'DB_DELETE': static_url + prefix + 'db-red.svg', 'DB_IN_PROGRESS': static_url + prefix + 'db-gray.gif', 'DB_INIT': static_url + prefix + 'db-gray.svg', 'DB_COMPLETE': static_url + prefix + 'db-green.svg', 'STACK_FAILED': static_url + prefix + 'stack-red.svg', 'STACK_DELETE': static_url + prefix + 'stack-red.svg', 'STACK_IN_PROGRESS': static_url + prefix + 'stack-gray.gif', 'STACK_INIT': static_url + prefix + 'stack-gray.svg', 'STACK_COMPLETE': static_url + prefix + 'stack-green.svg', 'SERVER_FAILED': static_url + prefix + 'server-red.svg', 'SERVER_DELETE': static_url + prefix + 'server-red.svg', 'SERVER_IN_PROGRESS': static_url + prefix + 'server-gray.gif', 'SERVER_INIT': static_url + prefix + 'server-gray.svg', 'SERVER_COMPLETE': static_url + prefix + 'server-green.svg', 'ALARM_FAILED': static_url + prefix + 'alarm-red.svg', 'ALARM_DELETE': static_url + prefix + 'alarm-red.svg', 'ALARM_IN_PROGRESS': static_url + prefix + 'alarm-gray.gif', 'ALARM_INIT': static_url + prefix + 'alarm-gray.svg', 'ALARM_COMPLETE': static_url + prefix + 'alarm-green.svg', 'VOLUME_FAILED': static_url + prefix + 'volume-red.svg', 'VOLUME_DELETE': static_url + prefix + 'volume-red.svg', 'VOLUME_IN_PROGRESS': static_url + prefix + 'volume-gray.gif', 'VOLUME_INIT': static_url + prefix + 'volume-gray.svg', 'VOLUME_COMPLETE': static_url + prefix + 'volume-green.svg', 'IMAGE_FAILED': static_url + prefix + 'image-red.svg', 'IMAGE_DELETE': static_url + prefix + 'image-red.svg', 'IMAGE_IN_PROGRESS': static_url + prefix + 'image-gray.gif', 'IMAGE_INIT': static_url + prefix + 'image-gray.svg', 'IMAGE_COMPLETE': static_url + prefix + 'image-green.svg', 'WAIT_FAILED': static_url + prefix + 'wait-red.svg', 'WAIT_DELETE': static_url + prefix + 'wait-red.svg', 'WAIT_IN_PROGRESS': static_url + prefix + 'wait-gray.gif', 'WAIT_INIT': static_url + prefix + 'wait-gray.svg', 'WAIT_COMPLETE': static_url + prefix + 'wait-green.svg', 'FIREWALL_FAILED': static_url + prefix + 'firewall-red.svg', 'FIREWALL_DELETE': static_url + prefix + 'firewall-red.svg', 'FIREWALL_IN_PROGRESS': static_url + prefix + 'firewall-gray.gif', 'FIREWALL_INIT': static_url + prefix + 'firewall-gray.svg', 'FIREWALL_COMPLETE': static_url + prefix + 'firewall-green.svg', 'FLOATINGIP_FAILED': static_url + prefix + 'floatingip-red.svg', 'FLOATINGIP_DELETE': static_url + prefix + 'floatingip-red.svg', 'FLOATINGIP_IN_PROGRESS': static_url + prefix + 'floatingip-gray.gif', 'FLOATINGIP_INIT': static_url + prefix + 'floatingip-gray.svg', 'FLOATINGIP_COMPLETE': static_url + prefix + 'floatingip-green.svg', 'ROUTER_FAILED': static_url + prefix + 'router-red.svg', 'ROUTER_DELETE': static_url + prefix + 'router-red.svg', 'ROUTER_IN_PROGRESS': static_url + prefix + 'router-gray.gif', 'ROUTER_INIT': static_url + prefix + 'router-gray.svg', 'ROUTER_COMPLETE': static_url + prefix + 'router-green.svg', 'POLICY_FAILED': static_url + prefix + 'policy-red.svg', 'POLICY_DELETE': static_url + prefix + 'policy-red.svg', 'POLICY_IN_PROGRESS': static_url + prefix + 'policy-gray.gif', 'POLICY_INIT': static_url + prefix + 'policy-gray.svg', 'POLICY_COMPLETE': static_url + prefix + 'policy-green.svg', 'CONFIG_FAILED': static_url + prefix + 'config-red.svg', 'CONFIG_DELETE': static_url + prefix + 'config-red.svg', 'CONFIG_IN_PROGRESS': static_url + prefix + 'config-gray.gif', 'CONFIG_INIT': static_url + prefix + 'config-gray.svg', 'CONFIG_COMPLETE': static_url + prefix + 'config-green.svg', 'NETWORK_FAILED': static_url + prefix + 'network-red.svg', 'NETWORK_DELETE': static_url + prefix + 'network-red.svg', 'NETWORK_IN_PROGRESS': static_url + prefix + 'network-gray.gif', 'NETWORK_INIT': static_url + prefix + 'network-gray.svg', 'NETWORK_COMPLETE': static_url + prefix + 'network-green.svg', 'PORT_FAILED': static_url + prefix + 'port-red.svg', 'PORT_DELETE': static_url + prefix + 'port-red.svg', 'PORT_IN_PROGRESS': static_url + prefix + 'port-gray.gif', 'PORT_INIT': static_url + prefix + 'port-gray.svg', 'PORT_COMPLETE': static_url + prefix + 'port-green.svg', 'SECURITYGROUP_FAILED': static_url + prefix + 'securitygroup-red.svg', 'SECURITYGROUP_DELETE': static_url + prefix + 'securitygroup-red.svg', 'SECURITYGROUP_IN_PROGRESS': static_url + prefix + 'securitygroup-gray.gif', 'SECURITYGROUP_INIT': static_url + prefix + 'securitygroup-gray.svg', 'SECURITYGROUP_COMPLETE': static_url + prefix + 'securitygroup-green.svg', 'VPN_FAILED': static_url + prefix + 'vpn-red.svg', 'VPN_DELETE': static_url + prefix + 'vpn-red.svg', 'VPN_IN_PROGRESS': static_url + prefix + 'vpn-gray.gif', 'VPN_INIT': static_url + prefix + 'vpn-gray.svg', 'VPN_COMPLETE': static_url + prefix + 'vpn-green.svg', 'FLAVOR_FAILED': static_url + prefix + 'flavor-red.svg', 'FLAVOR_DELETE': static_url + prefix + 'flavor-red.svg', 'FLAVOR_IN_PROGRESS': static_url + prefix + 'flavor-gray.gif', 'FLAVOR_INIT': static_url + prefix + 'flavor-gray.svg', 'FLAVOR_COMPLETE': static_url + prefix + 'flavor-green.svg', 'KEYPAIR_FAILED': static_url + prefix + 'keypair-red.svg', 'KEYPAIR_DELETE': static_url + prefix + 'keypair-red.svg', 'KEYPAIR_IN_PROGRESS': static_url + prefix + 'keypair-gray.gif', 'KEYPAIR_INIT': static_url + prefix + 'keypair-gray.svg', 'KEYPAIR_COMPLETE': static_url + prefix + 'keypair-green.svg', 'UNKNOWN_FAILED': static_url + prefix + 'unknown-red.svg', 'UNKNOWN_DELETE': static_url + prefix + 'unknown-red.svg', 'UNKNOWN_IN_PROGRESS': static_url + prefix + 'unknown-gray.gif', 'UNKNOWN_INIT': static_url + prefix + 'unknown-gray.svg', 'UNKNOWN_COMPLETE': static_url + prefix + 'unknown-green.svg', } resource_types = { # LB 'LoadBalance': 'LB', 'HealthMonitor': 'LB', 'PoolMember': 'LB', 'Pool': 'LB', # DB 'DBInstance': 'DB', 'Database': 'DB', # SERVER 'Instance': 'SERVER', 'Server': 'SERVER', # ALARM 'Alarm': 'ALARM', 'CombinationAlarm': 'ALARM', 'CWLiteAlarm': 'ALARM', # VOLUME 'Volume': 'VOLUME', 'VolumeAttachment': 'VOLUME', # STACK 'stack': 'STACK', 'AutoScalingGroup': 'STACK', 'InstanceGroup': 'STACK', 'ServerGroup': 'STACK', 'ResourceGroup': 'STACK', # IMAGE 'Image': 'IMAGE', # WAIT 'WaitCondition': 'WAIT', 'WaitConditionHandle': 'WAIT', 'UpdateWaitConditionHandle': 'WAIT', # FIREWALL 'Firewall': 'FIREWALL', 'FirewallPolicy': 'FIREWALL', 'FirewallRule': 'FIREWALL', # FLOATINGIP 'FloatingIP': 'FLOATINGIP', 'FloatingIPAssociation': 'FLOATINGIP', # ROUTER 'Router': 'ROUTER', 'RouterGateway': 'ROUTER', 'RouterInterface': 'ROUTER', # POLICY 'ScalingPolicy': 'POLICY', # CONFIG 'CloudConfig': 'CONFIG', 'MultipartMime': 'CONFIG', 'SoftwareConfig': 'CONFIG', 'SoftwareDeployment': 'CONFIG', 'StructuredConfig': 'CONFIG', 'StructuredDeployment': 'CONFIG', # NETWORK 'Net': 'NETWORK', 'Subnet': 'NETWORK', 'NetworkGateway': 'NETWORK', 'ProviderNet': 'NETWORK', # PORT 'Port': 'PORT', # SECURITYGROUP 'SecurityGroup': 'SECURITYGROUP', # VPN 'VPNService': 'VPN', # FLAVOR 'Flavor': 'FLAVOR', # KEYPAIR 'KeyPair': 'KEYPAIR', } def get_resource_type(type): for key, value in resource_types.items(): if key in type: return value return 'UNKNOWN' def get_resource_status(status): if ('IN_PROGRESS' in status): return 'IN_PROGRESS' elif ('FAILED' in status): return 'FAILED' elif ('DELETE' in status): return 'DELETE' elif ('INIT' in status): return 'INIT' else: return 'COMPLETE' def get_resource_image(status, type): """Sets the image url and in_progress action sw based on status.""" resource_type = get_resource_type(type) resource_status = get_resource_status(status) resource_state = resource_type + "_" + resource_status for key in resource_images: if key == resource_state: return resource_images.get(key) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4874961 heat_dashboard-16.0.0/heat_dashboard/content/stacks/panel.py0000664000175000017500000000135215257164522022725 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ import horizon class Stacks(horizon.Panel): name = _("Stacks") slug = "stacks" permissions = ('openstack.services.orchestration',) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4874961 heat_dashboard-16.0.0/heat_dashboard/content/stacks/sro.py0000664000175000017500000000313015257164522022425 0ustar00zuulzuul# 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 django.template.defaultfilters import title from django.template.loader import render_to_string from horizon.utils import filters def stack_info(stack, stack_image): stack.stack_status_desc = title( filters.replace_underscores(stack.stack_status)) if stack.stack_status_reason: stack.stack_status_reason = title( filters.replace_underscores(stack.stack_status_reason) ) context = {} context['stack'] = stack context['stack_image'] = stack_image return render_to_string('project/stacks/_stack_info.html', context) def resource_info(resource): resource.resource_status_desc = title( filters.replace_underscores(resource.resource_status) ) if resource.resource_status_reason: resource.resource_status_reason = title( filters.replace_underscores(resource.resource_status_reason) ) context = {} context['resource'] = resource return render_to_string('project/stacks/_resource_info.html', context) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6614952 heat_dashboard-16.0.0/heat_dashboard/content/stacks/tables.py0000664000175000017500000003577515257164523023121 0ustar00zuulzuul# 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 django import urls from django.http import Http404 from django.template.defaultfilters import title from django.utils.translation import gettext_lazy as _ from django.utils.translation import ngettext_lazy from django.utils.translation import pgettext_lazy from heatclient import exc from horizon import messages from horizon import tables from horizon.utils import filters from heat_dashboard import api from heat_dashboard.content.stacks import mappings class LaunchStack(tables.LinkAction): name = "launch" verbose_name = _("Launch Stack") url = "horizon:project:stacks:select_template" classes = ("ajax-modal",) icon = "plus" policy_rules = (("orchestration", "stacks:validate_template"), ("orchestration", "stacks:create"),) class PreviewStack(tables.LinkAction): name = "preview" verbose_name = _("Preview Stack") url = "horizon:project:stacks:preview_template" classes = ("ajax-modal",) icon = "eye" policy_rules = (("orchestration", "stacks:validate_template"), ("orchestration", "stacks:preview"),) class CheckStack(tables.BatchAction): name = "check" verbose_name = _("Check Stack") policy_rules = (("orchestration", "actions:action"),) icon = "check-square" @staticmethod def action_present(count): return ngettext_lazy( "Check Stack", "Check Stacks", count ) @staticmethod def action_past(count): return ngettext_lazy( "Checked Stack", "Checked Stacks", count ) def action(self, request, stack_id): api.heat.action_check(request, stack_id) class SuspendStack(tables.BatchAction): name = "suspend" verbose_name = _("Suspend Stack") policy_rules = (("orchestration", "actions:action"),) icon = "pause" @staticmethod def action_present(count): return ngettext_lazy( "Suspend Stack", "Suspend Stacks", count ) @staticmethod def action_past(count): return ngettext_lazy( "Suspended Stack", "Suspended Stacks", count ) def action(self, request, stack_id): # api.heat.action_suspend(request, stack_id) api.heat.action_suspend(request, stack_id) class ResumeStack(tables.BatchAction): name = "resume" verbose_name = _("Resume Stack") policy_rules = (("orchestration", "actions:action"),) icon = "play" @staticmethod def action_present(count): return ngettext_lazy( "Resume Stack", "Resume Stacks", count ) @staticmethod def action_past(count): return ngettext_lazy( "Resumed Stack", "Resumed Stacks", count ) def action(self, request, stack_id): # api.heat.action_resume(request, stack_id) api.heat.action_resume(request, stack_id) class ChangeStackTemplate(tables.LinkAction): name = "edit" verbose_name = _("Change Stack Template") url = "horizon:project:stacks:change_template" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, stack): return urls.reverse(self.url, args=[stack.id]) class DeleteStack(tables.DeleteAction): @staticmethod def action_present(count): return ngettext_lazy( "Delete Stack", "Delete Stacks", count ) @staticmethod def action_past(count): return ngettext_lazy( "Deleted Stack", "Deleted Stacks", count ) policy_rules = (("orchestration", "stacks:delete"),) def delete(self, request, stack_id): api.heat.stack_delete(request, stack_id) def allowed(self, request, stack): if stack is not None: return stack.stack_status != 'DELETE_COMPLETE' return True class StacksUpdateRow(tables.Row): ajax = True def can_be_selected(self, datum): return datum.stack_status != 'DELETE_COMPLETE' def get_data(self, request, stack_id): try: stack = api.heat.stack_get(request, stack_id) if stack.stack_status == 'DELETE_COMPLETE': # returning 404 to the ajax call removes the # row from the table on the ui raise Http404 return stack except Http404: raise except Exception as e: messages.error(request, e) raise class StacksFilterAction(tables.FilterAction): filter_type = 'server' filter_choices = (('name', _('Stack Name ='), True, _('Case-sensitive')), ('id', _('Stack ID ='), True), ('status', _('Status ='), True)) class StacksTable(tables.DataTable): STATUS_CHOICES = ( ("Complete", True), ("Failed", False), ) STACK_STATUS_DISPLAY_CHOICES = ( ("init_in_progress", pgettext_lazy("current status of stack", "Init In Progress")), ("init_complete", pgettext_lazy("current status of stack", "Init Complete")), ("init_failed", pgettext_lazy("current status of stack", "Init Failed")), ("create_in_progress", pgettext_lazy("current status of stack", "Create In Progress")), ("create_complete", pgettext_lazy("current status of stack", "Create Complete")), ("create_failed", pgettext_lazy("current status of stack", "Create Failed")), ("delete_in_progress", pgettext_lazy("current status of stack", "Delete In Progress")), ("delete_complete", pgettext_lazy("current status of stack", "Delete Complete")), ("delete_failed", pgettext_lazy("current status of stack", "Delete Failed")), ("update_in_progress", pgettext_lazy("current status of stack", "Update In Progress")), ("update_complete", pgettext_lazy("current status of stack", "Update Complete")), ("update_failed", pgettext_lazy("current status of stack", "Update Failed")), ("rollback_in_progress", pgettext_lazy("current status of stack", "Rollback In Progress")), ("rollback_complete", pgettext_lazy("current status of stack", "Rollback Complete")), ("rollback_failed", pgettext_lazy("current status of stack", "Rollback Failed")), ("suspend_in_progress", pgettext_lazy("current status of stack", "Suspend In Progress")), ("suspend_complete", pgettext_lazy("current status of stack", "Suspend Complete")), ("suspend_failed", pgettext_lazy("current status of stack", "Suspend Failed")), ("resume_in_progress", pgettext_lazy("current status of stack", "Resume In Progress")), ("resume_complete", pgettext_lazy("current status of stack", "Resume Complete")), ("resume_failed", pgettext_lazy("current status of stack", "Resume Failed")), ("adopt_in_progress", pgettext_lazy("current status of stack", "Adopt In Progress")), ("adopt_complete", pgettext_lazy("current status of stack", "Adopt Complete")), ("adopt_failed", pgettext_lazy("current status of stack", "Adopt Failed")), ("snapshot_in_progress", pgettext_lazy("current status of stack", "Snapshot In Progress")), ("snapshot_complete", pgettext_lazy("current status of stack", "Snapshot Complete")), ("snapshot_failed", pgettext_lazy("current status of stack", "Snapshot Failed")), ("check_in_progress", pgettext_lazy("current status of stack", "Check In Progress")), ("check_complete", pgettext_lazy("current status of stack", "Check Complete")), ("check_failed", pgettext_lazy("current status of stack", "Check Failed")), ) name = tables.Column("stack_name", verbose_name=_("Stack Name"), link="horizon:project:stacks:detail",) created = tables.Column("creation_time", verbose_name=_("Created"), filters=(filters.parse_isotime, filters.timesince_sortable), attrs={'data-type': 'timesince'}) updated = tables.Column("updated_time", verbose_name=_("Updated"), filters=(filters.parse_isotime, filters.timesince_or_never)) status = tables.Column("status", hidden=True, status=True, status_choices=STATUS_CHOICES) stack_status = tables.Column("stack_status", verbose_name=_("Status"), display_choices=STACK_STATUS_DISPLAY_CHOICES) def get_object_display(self, stack): return stack.stack_name class Meta: name = "stacks" verbose_name = _("Stacks") pagination_param = 'stack_marker' status_columns = ["status", ] row_class = StacksUpdateRow table_actions_menu = (CheckStack, SuspendStack, ResumeStack,) table_actions = (LaunchStack, PreviewStack, DeleteStack, StacksFilterAction,) row_actions = (CheckStack, SuspendStack, ResumeStack, ChangeStackTemplate, DeleteStack,) def get_resource_url(obj): if obj.physical_resource_id == obj.stack_id: return None return urls.reverse('horizon:project:stacks:resource', args=(obj.stack_id, obj.resource_name)) class EventsTable(tables.DataTable): logical_resource = tables.Column('resource_name', verbose_name=_("Stack Resource"), link=get_resource_url) physical_resource = tables.Column('physical_resource_id', verbose_name=_("Resource")) timestamp = tables.Column('event_time', verbose_name=_("Time Since Event"), filters=(filters.parse_isotime, filters.timesince_or_never)) status = tables.Column("resource_status", filters=(title, filters.replace_underscores), verbose_name=_("Status"),) statusreason = tables.Column("resource_status_reason", verbose_name=_("Status Reason"),) class Meta: name = "events" verbose_name = _("Stack Events") pagination_param = 'event_marker' class ResourcesUpdateRow(tables.Row): ajax = True def get_data(self, request, resource_name): try: stack = self.table.stack stack_identifier = '{}/{}'.format(stack.stack_name, stack.id) # return api.heat.resource_get( # request, stack_identifier, resource_name) return api.heat.resource_get(request, stack_identifier, resource_name) except exc.HTTPNotFound: # returning 404 to the ajax call removes the # row from the table on the ui raise Http404 except Exception as e: messages.error(request, e) class ResourcesTable(tables.DataTable): class StatusColumn(tables.Column): def get_raw_data(self, datum): return datum.resource_status.partition("_")[2] STATUS_CHOICES = ( ("Complete", True), ("Failed", False), ) STATUS_DISPLAY_CHOICES = StacksTable.STACK_STATUS_DISPLAY_CHOICES logical_resource = tables.Column('resource_name', verbose_name=_("Stack Resource"), link=get_resource_url) physical_resource = tables.Column('physical_resource_id', verbose_name=_("Resource"), link=mappings.resource_to_url) resource_type = tables.Column("resource_type", verbose_name=_("Stack Resource Type"),) updated_time = tables.Column('updated_time', verbose_name=_("Date Updated"), filters=(filters.parse_isotime, filters.timesince_or_never)) status = tables.Column("resource_status", verbose_name=_("Status"), display_choices=STATUS_DISPLAY_CHOICES) statusreason = tables.Column("resource_status_reason", verbose_name=_("Status Reason"),) status_hidden = StatusColumn("status", hidden=True, status=True, status_choices=STATUS_CHOICES) def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs): super().__init__( request, data, needs_form_wrapper, **kwargs) self.stack = kwargs['stack'] def get_object_id(self, datum): return datum.resource_name class Meta: name = "resources" verbose_name = _("Stack Resources") status_columns = ["status_hidden", ] row_class = ResourcesUpdateRow ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6624951 heat_dashboard-16.0.0/heat_dashboard/content/stacks/tabs.py0000664000175000017500000001502715257164523022564 0ustar00zuulzuul# 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. import logging from operator import attrgetter from django.utils.translation import gettext_lazy as _ from horizon import messages from horizon import tabs from heat_dashboard import api from heat_dashboard.content.stacks import api as project_api from heat_dashboard.content.stacks import mappings from heat_dashboard.content.stacks import tables as project_tables from openstack_dashboard import policy LOG = logging.getLogger(__name__) class StackTopologyTab(tabs.Tab): name = _("Topology") slug = "topology" template_name = "project/stacks/_detail_topology.html" # template_name = "stacks/_detail_topology.html" preload = False def allowed(self, request): return policy.check( (("orchestration", "stacks:template"), ("orchestration", "stacks:lookup"), ("orchestration", "stacks:show"), ("orchestration", "resource:index"),), request) def get_context_data(self, request): context = {} stack = self.tab_group.kwargs['stack'] context['stack_id'] = stack.id context['d3_data'] = project_api.d3_data(request, stack_id=stack.id) return context class StackOverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "project/stacks/_detail_overview.html" def allowed(self, request): return policy.check( (("orchestration", "stacks:template"), ("orchestration", "stacks:lookup"), ("orchestration", "stacks:show"),), request) def get_context_data(self, request): return {"stack": self.tab_group.kwargs['stack']} class ResourceOverviewTab(tabs.Tab): name = _("Overview") slug = "resource_overview" template_name = "project/stacks/_resource_overview.html" def get_context_data(self, request): resource = self.tab_group.kwargs['resource'] resource_url = mappings.resource_to_url(resource) return { "resource": resource, "resource_url": resource_url, "metadata": self.tab_group.kwargs['metadata']} class StackEventsTab(tabs.TableTab): name = _("Events") slug = "events" table_classes = (project_tables.EventsTable, ) template_name = "project/stacks/_detail_events.html" preload = False def allowed(self, request): return policy.check( (("orchestration", "stacks:template"), ("orchestration", "stacks:lookup"), ("orchestration", "stacks:show"), ("orchestration", "events:index"),), request) def get_events_data(self): stack = self.tab_group.kwargs['stack'] stack_identifier = '{}/{}'.format(stack.stack_name, stack.id) prev_marker = self.request.GET.get( project_tables.EventsTable._meta.prev_pagination_param) if prev_marker is not None: sort_dir = 'asc' marker = prev_marker else: sort_dir = 'desc' marker = self.request.GET.get( project_tables.EventsTable._meta.pagination_param, None) try: events, self._more, self._prev = api.heat.events_list( self.request, stack_identifier, marker=marker, paginate=True, sort_dir=sort_dir) if prev_marker is not None: events = sorted(events, key=attrgetter('event_time'), reverse=True) LOG.debug('got events %s', events) # The stack id is needed to generate the resource URL. for event in events: event.stack_id = stack.id except Exception: events = [] self._prev = False self._more = False messages.error(self.request, _( 'Unable to get events for stack "%s".') % stack.stack_name) return events def has_prev_data(self, table): return self._prev def has_more_data(self, table): return self._more class StackResourcesTab(tabs.Tab): name = _("Resources") slug = "resources" template_name = "project/stacks/_detail_resources.html" preload = False def allowed(self, request): return policy.check( (("orchestration", "stacks:template"), ("orchestration", "stacks:lookup"), ("orchestration", "stacks:show"), ("orchestration", "resource:index"),), request) def get_context_data(self, request): stack = self.tab_group.kwargs['stack'] try: stack_identifier = '{}/{}'.format(stack.stack_name, stack.id) resources = api.heat.resources_list(self.request, stack_identifier) LOG.debug('got resources %s', resources) # The stack id is needed to generate the resource URL. for r in resources: r.stack_id = stack.id except Exception: resources = [] messages.error(request, _( 'Unable to get resources for stack "%s".') % stack.stack_name) return {"stack": stack, "table": project_tables.ResourcesTable( request, data=resources, stack=stack), } class StackTemplateTab(tabs.Tab): name = _("Template") slug = "stack_template" template_name = "project/stacks/_stack_template.html" def allowed(self, request): return policy.check( (("orchestration", "stacks:template"), ("orchestration", "stacks:lookup"), ("orchestration", "stacks:show"),), request) def get_context_data(self, request): return {"stack_template": self.tab_group.kwargs['stack_template']} class StackDetailTabs(tabs.TabGroup): slug = "stack_details" tabs = (StackTopologyTab, StackOverviewTab, StackResourcesTab, StackEventsTab, StackTemplateTab) sticky = True class ResourceDetailTabs(tabs.TabGroup): slug = "resource_details" tabs = (ResourceOverviewTab,) sticky = True ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.257263 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/0000775000175000017500000000000015257164606023254 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3082619 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/0000775000175000017500000000000015257164606024544 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_change_template.html0000664000175000017500000000071315257164522030707 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block form_attrs %}enctype="multipart/form-data"{% endblock %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "A template is used to automate the deployment of infrastructure, services, and applications." %}

{% trans "Use one of the available template source options to specify the template to be used in changing this stack." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_create.html0000664000175000017500000000032615257164522027032 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Create a new stack with the provided values." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_detail_events.html0000664000175000017500000000004415257164522030412 0ustar00zuulzuul{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_detail_overview.html0000664000175000017500000000334715257164522030765 0ustar00zuulzuul{% load i18n sizeformat %}
{% trans "Name" %}
{{ stack.stack_name }}
{% trans "ID" %}
{{ stack.id }}
{% trans "Description" %}
{{ stack.description }}

{% trans "Status" %}


{% trans "Created" %}
{{ stack.creation_time|parse_isotime|timesince_or_never }}
{% trans "Last Updated" %}
{{ stack.updated_time|parse_isotime|timesince_or_never }}
{% trans "Status" %}
{% blocktrans with stack_status_title=stack.stack_status|title stack_status_reason=stack.stack_status_reason %}{{ stack_status_title }}: {{ stack_status_reason }}{% endblocktrans %}

{% trans "Outputs" %}


{% for output in stack.outputs %}
{{ output.output_key }}
{{ output.description }}
{{ output.output_value|stack_output }}
{% endfor %}

{% trans "Stack Parameters" %}


{% for key, value in stack.parameters.items %}
{{ key }}
{{ value }}
{% endfor %}

{% trans "Launch Parameters" %}


{% trans "Timeout" %}
{{ stack.timeout_mins }} {% trans "Minutes" %}
{% trans "Rollback" %}
{% if stack.disable_rollback %}{% trans "Disabled" %}{% else %}{% trans "Enabled" %}{% endif %}
././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_detail_resources.html0000664000175000017500000000004415257164522031120 0ustar00zuulzuul{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_detail_topology.html0000664000175000017500000000044015257164522030762 0ustar00zuulzuul{% load i18n sizeformat %}
././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_preview.html0000664000175000017500000000032715257164522027251 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Preview a new stack with the provided values." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_preview_details.html0000664000175000017500000000305615257164522030760 0ustar00zuulzuul{% extends "horizon/common/_modal.html" %} {% load i18n %} {% block modal-header %}{% trans "Stack Preview" %}{% endblock %} {% block modal-body %}
{% for key, value in stack_preview.items %} {% if key != 'parameters' and key != 'resources' and key != 'links' %}
{{ key }}
{{ value }}
{% endif %} {% endfor %}
{% if stack_preview.parameters %}
{% trans "Parameters" %}

{% for key, value in stack_preview.parameters.items %}
{{ key }}
{{ value }}
{% endfor %}
{% endif %} {% if stack_preview.links %}
{% trans "Links" %}

{% for link in stack_preview.links %}
{{ link.rel }}
{{ link.href }}
{% endfor %} {% endif %} {% if stack_preview.resources %}
{% trans "Resources" %}
{% for resource in stack_preview.resources %}
{% for key, value in resource.items %}
{{ key }}
{{ value }}
{% endfor %}
{% endfor %} {% endif %}
{% endblock %} {% block modal-footer %} {% trans "Close" %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_preview_template.html0000664000175000017500000000053115257164522031141 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block form_attrs %}enctype="multipart/form-data"{% endblock %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Use one of the available template source options to specify the template to be used in previewing this stack." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_resource_info.html0000664000175000017500000000046615257164522030436 0ustar00zuulzuul

{{ resource.resource_name }}

{% if resource.resource_status == 'CREATE_FAILED' %}

{{ resource.resource_status_desc }}

{{ resource.resource_status_reason }}

{% else %}

{{ resource.resource_status_desc }}

{% endif %}

{{ resource.resource_type }}

././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_resource_overview.html0000664000175000017500000000224615257164522031347 0ustar00zuulzuul{% load i18n sizeformat %}
{% trans "Stack Resource ID" %}
{{ resource.resource_name }}
{% trans "Resource ID" %}
{% if resource_url %} {{ resource.physical_resource_id }} {% else %} {{ resource.physical_resource_id }} {% endif %}
{% trans "Stack Resource Type" %}
{{ resource.resource_type }}
{% trans "Description" %}
{{ resource.description }}

{% trans "Status" %}


{% trans "Last Updated" %}
{{ resource.updated_time|parse_isotime|timesince_or_never }}
{% trans "Status" %}
{% blocktrans with resource_status=resource.resource_status|title|replace_underscores resource_status_reason=resource.resource_status_reason %}{{ resource_status }}: {{ resource_status_reason }}{% endblocktrans %}

{% trans "Resource Metadata" %}


{{ metadata }}
  
././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_select_template.html0000664000175000017500000000151415257164522030741 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block form_attrs %}enctype="multipart/form-data"{% endblock %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "A template is used to automate the deployment of infrastructure, services, and applications." %}

{% trans "Use one of the available template source options to specify the template to be used in creating this stack." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4884963 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_stack_info.html0000664000175000017500000000076415257164522027715 0ustar00zuulzuul

{{ stack.stack_name }}

{{ stack.stack_status_desc }}

{% if stack.stack_status == 'CREATE_FAILED' %}

{{ stack.stack_status_reason }}

{% endif %} {% for output in stack.outputs %} {% if output.output_key == 'WebsiteURL' %} {{ output.description }} {% endif %} {% endfor %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_stack_template.html0000664000175000017500000000013215257164522030562 0ustar00zuulzuul{% load i18n sizeformat %}
{{ stack_template }}
././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/_update.html0000664000175000017500000000051115257164522027045 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Update a stack with the provided values. Please note that any encrypted parameters, such as passwords, will be reset to default if you do not change them here." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/change_template.html0000664000175000017500000000027715257164522030555 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Change Template" %}{% endblock %} {% block main %} {% include 'project/stacks/_change_template.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/create.html0000664000175000017500000000026315257164522026673 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Launch Stack" %}{% endblock %} {% block main %} {% include 'project/stacks/_create.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/index.html0000664000175000017500000000111415257164522026533 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{{ page_title }}{% endblock %} {% block main %}{{ table.render }}{% endblock %} {% block js %} {% include "horizon/_scripts.html" %} {% endblock %}././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/preview.html0000664000175000017500000000026515257164522027113 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Preview Stack" %}{% endblock %} {% block main %} {% include 'project/stacks/_preview.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/preview_details.html0000664000175000017500000000030515257164522030613 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Preview Stack Details" %}{% endblock %} {% block main %} {% include 'project/stacks/_preview_details.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/preview_template.html0000664000175000017500000000030115257164522030775 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Preview Template" %}{% endblock %} {% block main %} {% include 'project/stacks/_preview_template.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/select_template.html0000664000175000017500000000027715257164522030607 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Select Template" %}{% endblock %} {% block main %} {% include 'project/stacks/_select_template.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/templates/stacks/update.html0000664000175000017500000000027615257164522026716 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update Stack Parameters" %}{% endblock %} {% block main %} {% include 'project/stacks/_update.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/urls.py0000664000175000017500000000335615257164522022621 0ustar00zuulzuul# 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 django.urls import re_path from heat_dashboard.content.stacks import views urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(r'^select_template$', views.SelectTemplateView.as_view(), name='select_template'), re_path(r'^launch$', views.CreateStackView.as_view(), name='launch'), re_path(r'^preview_template$', views.PreviewTemplateView.as_view(), name='preview_template'), re_path(r'^preview$', views.PreviewStackView.as_view(), name='preview'), re_path(r'^preview_details$', views.PreviewStackDetailsView.as_view(), name='preview_details'), re_path(r'^stack/(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), re_path(r'^(?P[^/]+)/change_template$', views.ChangeTemplateView.as_view(), name='change_template'), re_path(r'^(?P[^/]+)/edit_stack$', views.EditStackView.as_view(), name='edit_stack'), re_path(r'^stack/(?P[^/]+)/(?P[^/]+)/$', views.ResourceView.as_view(), name='resource'), re_path(r'^get_d3_data/(?P[^/]+)/$', views.JSONView.as_view(), name='d3_data'), ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4894962 heat_dashboard-16.0.0/heat_dashboard/content/stacks/views.py0000664000175000017500000003125315257164522022766 0ustar00zuulzuul# 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. import json from operator import attrgetter import yaml from django.http import HttpResponse from django.urls import reverse from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ import django.views.generic from heat_dashboard import api from heat_dashboard.content.stacks import api as project_api from heat_dashboard.content.stacks import forms as project_forms from heat_dashboard.content.stacks import tables as project_tables from heat_dashboard.content.stacks import tabs as project_tabs from horizon import exceptions from horizon import forms from horizon import tables from horizon import tabs from horizon.utils import memoized from horizon import views class IndexView(tables.DataTableView): table_class = project_tables.StacksTable page_title = _("Stacks") template_name = 'project/stacks/index.html' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._more = None def has_prev_data(self, table): return self._prev def has_more_data(self, table): return self._more def get_data(self): stacks = [] filters = self.get_filters() prev_marker = self.request.GET.get( project_tables.StacksTable._meta.prev_pagination_param) if prev_marker is not None: sort_dir = 'asc' marker = prev_marker else: sort_dir = 'desc' marker = self.request.GET.get( project_tables.StacksTable._meta.pagination_param) try: stacks, self._more, self._prev = api.heat.stacks_list( self.request, marker=marker, paginate=True, sort_dir=sort_dir, filters=filters) if prev_marker is not None: stacks = sorted(stacks, key=attrgetter('creation_time'), reverse=True) except Exception: self._prev = False self._more = False msg = _('Unable to retrieve stacks list.') exceptions.handle(self.request, msg) return stacks class SelectTemplateView(forms.ModalFormView): template_name = 'project/stacks/select_template.html' form_id = "select_template" form_class = project_forms.TemplateForm submit_label = _("Next") submit_url = reverse_lazy("horizon:project:stacks:select_template") success_url = reverse_lazy('horizon:project:stacks:launch') page_title = _("Select Template") def get_initial(self): initial = {} for name in [ 'template_url', 'template_source', 'template_data', 'environment_source', 'environment_data' ]: tmp = self.request.GET.get(name) if tmp: initial[name] = tmp return initial def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['next_view'] = CreateStackView return kwargs class ChangeTemplateView(forms.ModalFormView): template_name = 'project/stacks/change_template.html' form_id = "change_template" form_class = project_forms.ChangeTemplateForm submit_label = _("Next") submit_url = "horizon:project:stacks:change_template" cancel_url = reverse_lazy('horizon:project:stacks:index') success_url = reverse_lazy('horizon:project:stacks:edit_stack') page_title = _("Change Template") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) args = (self.get_object().id,) context['submit_url'] = reverse(self.submit_url, args=args) return context @memoized.memoized_method def get_object(self): stack_id = self.kwargs['stack_id'] try: self._stack = api.heat.stack_get(self.request, stack_id) except Exception: msg = _("Unable to retrieve stack.") redirect = reverse('horizon:project:stacks:index') exceptions.handle(self.request, msg, redirect=redirect) return self._stack def get_initial(self): stack = self.get_object() return {'stack_id': stack.id, 'stack_name': stack.stack_name } def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['next_view'] = EditStackView return kwargs class PreviewTemplateView(forms.ModalFormView): template_name = 'project/stacks/preview_template.html' form_id = "preview_template" form_class = project_forms.PreviewTemplateForm submit_label = _("Next") submit_url = reverse_lazy('horizon:project:stacks:preview_template') success_url = reverse_lazy('horizon:project:stacks:preview') page_title = _("Preview Template") def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['next_view'] = PreviewStackView return kwargs class CreateStackView(forms.ModalFormView): template_name = 'project/stacks/create.html' form_id = "launch_stack" form_class = project_forms.CreateStackForm submit_label = _("Launch") submit_url = reverse_lazy("horizon:project:stacks:launch") success_url = reverse_lazy('horizon:project:stacks:index') page_title = _("Launch Stack") def get_initial(self): initial = {} if 'environment_data' in self.kwargs: initial['environment_data'] = self.kwargs['environment_data'] if 'parameters' in self.kwargs: parameters = self.kwargs['parameters'] # if instead of a dict we got a bytestring of # serialized json we don't double dumps it if isinstance(parameters, bytes): initial['parameters'] = parameters else: initial['parameters'] = json.dumps(parameters) return initial def get_form_kwargs(self): kwargs = super().get_form_kwargs() if 'parameters' in self.kwargs: kwargs['parameters'] = self.kwargs['parameters'] else: data = json.loads(self.request.POST['parameters']) kwargs['parameters'] = data return kwargs # edit stack parameters, coming from template selector class EditStackView(CreateStackView): template_name = 'project/stacks/update.html' form_id = "update_stack" form_class = project_forms.EditStackForm submit_label = _("Update") submit_url = "horizon:project:stacks:edit_stack" success_url = reverse_lazy('horizon:project:stacks:index') page_title = _("Update Stack") def get_initial(self): initial = super().get_initial() initial['stack'] = self.get_object()['stack'] if initial['stack']: initial['stack_id'] = initial['stack'].id initial['stack_name'] = initial['stack'].stack_name return initial def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) args = (self.get_object()['stack'].id,) context['submit_url'] = reverse(self.submit_url, args=args) return context @memoized.memoized_method def get_object(self): stack_id = self.kwargs['stack_id'] try: stack = {} stack['stack'] = api.heat.stack_get(self.request, stack_id) stack['template'] = api.heat.template_get(self.request, stack_id) self._stack = stack except Exception: msg = _("Unable to retrieve stack.") redirect = reverse('horizon:project:stacks:index') exceptions.handle(self.request, msg, redirect=redirect) return self._stack class PreviewStackView(CreateStackView): template_name = 'project/stacks/preview.html' form_id = "preview_stack" form_class = project_forms.PreviewStackForm submit_label = _("Preview") submit_url = reverse_lazy('horizon:project:stacks:preview') success_url = reverse_lazy('horizon:project:stacks:index') page_title = _("Preview Stack") def get_form_kwargs(self): kwargs = super(CreateStackView, self).get_form_kwargs() kwargs['next_view'] = PreviewStackDetailsView return kwargs class PreviewStackDetailsView(forms.ModalFormMixin, views.HorizonTemplateView): template_name = 'project/stacks/preview_details.html' page_title = _("Preview Stack Details") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['stack_preview'] = self.kwargs['stack_preview'].to_dict() return context class DetailView(tabs.TabView): tab_group_class = project_tabs.StackDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ stack.stack_name|default:stack.id }}" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) stack = self.get_data(self.request, **kwargs) table = project_tables.StacksTable(self.request) context["stack"] = stack context["url"] = self.get_redirect_url() context["actions"] = table.render_row_actions(stack) return context @memoized.memoized_method def get_data(self, request, **kwargs): stack_id = kwargs['stack_id'] try: stack = api.heat.stack_get(request, stack_id) request.session['stack_id'] = stack.id request.session['stack_name'] = stack.stack_name return stack except Exception: msg = _("Unable to retrieve stack.") exceptions.handle(request, msg, redirect=self.get_redirect_url()) @memoized.memoized_method def get_template(self, request, **kwargs): try: stack_template = api.heat.template_get( request, kwargs['stack_id']) return yaml.safe_dump(stack_template, indent=2) except Exception: msg = _("Unable to retrieve stack template.") exceptions.handle(request, msg, redirect=self.get_redirect_url()) def get_tabs(self, request, **kwargs): stack = self.get_data(request, **kwargs) stack_template = self.get_template(request, **kwargs) return self.tab_group_class( request, stack=stack, stack_template=stack_template, **kwargs) @staticmethod def get_redirect_url(): return reverse('horizon:project:stacks:index') class ResourceView(tabs.TabView): tab_group_class = project_tabs.ResourceDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ resource.resource_name|"\ "default:resource.logical_resource_id }}" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["resource"] = self.get_data(self.request, **kwargs) context["metadata"] = self.get_metadata(self.request, **kwargs) return context @memoized.memoized_method def get_data(self, request, **kwargs): try: resource = api.heat.resource_get( request, kwargs['stack_id'], kwargs['resource_name']) return resource except Exception: msg = _("Unable to retrieve resource.") redirect = reverse('horizon:project:stacks:index') exceptions.handle(request, msg, redirect=redirect) @memoized.memoized_method def get_metadata(self, request, **kwargs): try: metadata = api.heat.resource_metadata_get( request, kwargs['stack_id'], kwargs['resource_name']) return json.dumps(metadata, indent=2) except Exception: msg = _("Unable to retrieve metadata.") redirect = reverse('horizon:project:stacks:index') exceptions.handle(request, msg, redirect=redirect) def get_tabs(self, request, **kwargs): resource = self.get_data(request, **kwargs) metadata = self.get_metadata(request, **kwargs) return self.tab_group_class( request, resource=resource, metadata=metadata, **kwargs) class JSONView(django.views.generic.View): def get(self, request, stack_id=''): return HttpResponse(project_api.d3_data(request, stack_id=stack_id), content_type="application/json") ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3092618 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/0000775000175000017500000000000015257164606023647 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4904962 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/__init__.py0000664000175000017500000000000015257164522025743 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4904962 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/api.py0000664000175000017500000002076015257164522024774 0ustar00zuulzuul# 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. import json from functools import wraps from multiprocessing import pool from django.conf import settings from openstack_dashboard import api as dashboard_api from heat_dashboard import api as heat_api try: API_TIMEOUT = settings.HEAT_TEMPLATE_GENERATOR_API_TIMEOUT except AttributeError: API_TIMEOUT = 60 try: API_PARALLEL = settings.HEAT_TEMPLATE_GENERATOR_API_PARALLEL except AttributeError: API_PARALLEL = 2 def handle_exception(func): @wraps(func) def wrapped(*args, **kwargs): ret, err = None, None try: ret = func(*args, **kwargs) except Exception as error: err = str(error) return ret if ret else [], err return wrapped @handle_exception def get_networks(request): return dashboard_api.neutron.network_list(request) @handle_exception def get_subnets(request): return dashboard_api.neutron.subnet_list(request) @handle_exception def get_volume_ids(request): return [{'id': vol.id, 'name': vol.name if vol.name else '(%s)' % vol.id} for vol in dashboard_api.cinder.volume_list(request)] @handle_exception def get_volume_snapshots(request): return [{'id': volsnap.id, 'name': volsnap.name if volsnap.name else '(%s)' % volsnap.id[:6]} for volsnap in dashboard_api.cinder.volume_snapshot_list(request)] @handle_exception def get_volume_types(request): return [{'id': voltype.id, 'name': voltype.name if voltype.name else '(%s)' % voltype.id[:6]} for voltype in dashboard_api.cinder.volume_type_list(request)] @handle_exception def get_volume_backups(request): return [{'id': volbackup.id, 'name': volbackup.name if volbackup.name else '(%s)' % volbackup.id[:6]} for volbackup in dashboard_api.cinder.volume_backup_list(request)] @handle_exception def get_images(request): images = dashboard_api.glance.image_list_detailed(request) if isinstance(images, tuple): images = images[0] return [{'id': img.id, 'name': img.name if img.name else '(%s)' % img.id[:6]} for img in images] @handle_exception def get_floatingips(request): return [{'id': fip.id, 'name': fip.floating_ip_address} for fip in dashboard_api.neutron.tenant_floating_ip_list( request, True)] @handle_exception def get_ports(request): return [{'id': port.id, 'name': port.name if port.name else '(%s)' % port.id[:6]} for port in dashboard_api.neutron.port_list(request)] @handle_exception def get_security_groups(request): return [{'id': secgroup.id, 'name': secgroup.name if secgroup.name else '(%s)' % secgroup.id[:6]} for secgroup in dashboard_api.neutron.security_group_list(request)] @handle_exception def get_routers(request): return [{'id': router.id, 'name': router.name if router.name else '(%s)' % router.id[:6]} for router in dashboard_api.neutron.router_list(request)] @handle_exception def get_qos_policies(request): return [{'id': policy.id, 'name': policy.name if policy.name else '(%s)' % policy.id[:6]} for policy in dashboard_api.neutron.policy_list(request)] @handle_exception def get_availability_zones(request): return [{'id': az.name, 'name': az.name} for az in dashboard_api.nova.availability_zone_list(request)] @handle_exception def get_flavors(request): return [{'id': flavor.name, 'name': flavor.name} for flavor in dashboard_api.nova.flavor_list(request)] @handle_exception def get_instances(request): servers = dashboard_api.nova.server_list(request) if isinstance(servers, tuple): servers = servers[0] return [{'id': server.id, 'name': server.name if server.name else '(%s)' % server.id[:6]} for server in servers] @handle_exception def get_keypairs(request): return [{'name': keypair.name} for keypair in dashboard_api.nova.keypair_list(request)] @handle_exception def get_template_versions(request): return [{'name': version.version, 'id': version.version} for version in heat_api.heat.template_version_list(request) if version.type == 'hot'] class APIThread: thread_pool = pool.ThreadPool(processes=API_PARALLEL) async_results = {} def add_thread(self, apikey, func, args): self.async_results[apikey] = self.thread_pool.apply_async(func, args) def get_async_result(self, apikey): if apikey not in self.async_results: return [], None try: ret, err = self.async_results[apikey].get( timeout=API_TIMEOUT) except Exception as error: ret, err = [], str(error) return ret, err def _get_network_resources(options, all_networks): try: if all_networks: options['networks'] = [ {'id': nw.id, 'name': nw.name if nw.name else '(%s)' % nw.id[: 6]} for nw in all_networks if not getattr(nw, 'is_router_external')] options['floating_networks'] = [ {'id': nw.id, 'name': nw.name if nw.name else '(%s)' % nw.id[: 6]} for nw in all_networks if getattr(nw, 'is_router_external')] else: options['networks'] = [] options['floating_networks'] = [] except Exception: options['networks'] = [] options['floating_networks'] = [] def _get_subnet_resources(options, all_subnets): try: if all_subnets and options.get('floating_networks'): floating_network_ids = [nw.get('id') for nw in options['floating_networks']] options['subnets'] = [{'id': sb.id, 'name': sb.name} for sb in all_subnets if sb.network_id not in floating_network_ids] options['floating_subnets'] = [ {'id': subnet.id, 'name': subnet.name} for subnet in all_subnets if subnet.network_id in floating_network_ids] else: options['subnets'] = [] options['floating_subnets'] = [] except Exception: options['subnets'] = [] options['floating_subnets'] = [] def get_resource_options(request): api_threads = APIThread() api_mapping = { 'volumes': get_volume_ids, 'volume_snapshots': get_volume_snapshots, 'volume_types': get_volume_types, 'volume_backups': get_volume_backups, 'images': get_images, 'floatingips': get_floatingips, 'networks': get_networks, 'subnets': get_subnets, 'ports': get_ports, 'security_groups': get_security_groups, 'routers': get_routers, 'qos_policies': get_qos_policies, 'availability_zones': get_availability_zones, 'flavors': get_flavors, 'instances': get_instances, 'keypairs': get_keypairs, 'template_versions': get_template_versions, } options = {} errors = {} for resource, method in api_mapping.items(): api_threads.add_thread(resource, method, args=(request,)) for resource in api_mapping.keys(): ret, err = api_threads.get_async_result(resource) options[resource] = ret if err: errors[resource.replace('_', ' ').capitalize()] = err all_networks = options.pop('networks') _get_network_resources(options, all_networks) all_subnets = options.pop('subnets') _get_subnet_resources(options, all_subnets) role_names = [] for role in request.user.roles: role_names.append(role.get('name')) options.update({ 'auth': { 'tenant_id': request.user.tenant_id, 'admin': 'admin' in role_names, }, }) if len(errors.keys()) > 0: options.update({'errors': errors}) return json.dumps(options) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/panel.py0000664000175000017500000000152015257164522025313 0ustar00zuulzuul# Copyright 2017 NTT Communications. # # 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 django.utils.translation import gettext_lazy as _ import horizon class TemplateGenerator(horizon.Panel): name = _("Template Generator") slug = 'template_generator' permissions = ('openstack.services.orchestration',) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.258263 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/templates/0000775000175000017500000000000015257164606025645 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3092618 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/templates/template_generator/0000775000175000017500000000000015257164606031526 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00113 path=heat_dashboard-16.0.0/heat_dashboard/content/template_generator/templates/template_generator/index.html 27 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/templates/template_generator/index.h0000664000175000017500000001617115257164522033011 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{{ title }}{% endblock %} {% block page_header %} {% endblock %} {% block main %}
{$ tmptversion.name $}
{% trans "Template Generator" %}
{% trans "Manage Drafts" %} {% trans "Save Draft" %} {% trans "Load Draft" %} {% trans "Import Draft" %} {% trans "Export Draft" %}
{% trans "Clear Canvas" %}
{$ level $}
{% endblock %} ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/urls.py0000664000175000017500000000145315257164522025206 0ustar00zuulzuul# 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 django.urls import re_path from heat_dashboard.content.template_generator import views urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(r'^get_resource_options$', views.OptionView.as_view(), name="apis"), ] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_generator/views.py0000664000175000017500000000213315257164522025352 0ustar00zuulzuul# 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 django.http import HttpResponse # noqa from django.utils.translation import gettext_lazy as _ from django.views import generic from horizon.browsers.views import AngularIndexView from heat_dashboard.content.template_generator import api class IndexView(AngularIndexView): template_name = 'project/template_generator/index.html' page_title = _("Template Generator") class OptionView(generic.View): def get(self, request): return HttpResponse(api.get_resource_options(request), content_type="application/json") ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.311262 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/0000775000175000017500000000000015257164606023531 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/__init__.py0000664000175000017500000000000015257164522025625 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/panel.py0000664000175000017500000000152415257164522025201 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ import horizon class TemplateVersions(horizon.Panel): name = _("Template Versions") slug = "template_versions" permissions = ('openstack.services.orchestration',) policy_rules = (("orchestration", "stacks:list_template_versions"),) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/tables.py0000664000175000017500000000334215257164522025354 0ustar00zuulzuul# 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 django.template import defaultfilters as filters from django.utils.translation import gettext_lazy as _ from horizon import tables class TemplateVersionsTable(tables.DataTable): version = tables.Column( "version", verbose_name=_("Version"), link="horizon:project:template_versions:details",) type = tables.Column( "type", verbose_name=_("Type"), filters=(filters.upper,)) def get_object_id(self, template_versions): return template_versions.version class Meta: name = "template_versions" table_actions = (tables.FilterAction,) verbose_name = _("Template Versions") table_actions = (tables.FilterAction,) multi_select = False class TemplateFunctionsTable(tables.DataTable): functions = tables.Column('functions', verbose_name=_("Function")) description = tables.Column('description', verbose_name=_("Description")) def get_object_id(self, template_functions): return template_functions.functions class Meta: name = "template_functions" verbose_name = _("Template Functions") table_actions = (tables.FilterAction,) multi_select = False ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/tabs.py0000664000175000017500000000345415257164522025037 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ from horizon import messages from horizon import tabs from openstack_dashboard import policy from heat_dashboard import api from heat_dashboard.content.template_versions import tables as project_tables class TemplateFunctionsTab(tabs.Tab): name = _("Template Functions") slug = "template_functions" template_name = "project/template_versions/_details.html" preload = False def allowed(self, request): return policy.check( (("orchestration", "stacks:list_template_functions"),), request) def get_context_data(self, request): template_version = self.tab_group.kwargs['template_version'] try: template_functions = api.heat.template_function_list( self.request, template_version) except Exception: template_functions = [] messages.error(request, _('Unable to get functions for template ' 'version "%s".') % template_version) return {"table": project_tables.TemplateFunctionsTable( request, data=template_functions), } class TemplateVersionDetailsTabs(tabs.TabGroup): slug = "template_version_details" tabs = (TemplateFunctionsTab,) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.259263 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/0000775000175000017500000000000015257164606025527 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3122618 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/stacks.template_versions/0000775000175000017500000000000015257164606032561 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000022400000000000010213 xustar00121 path=heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/stacks.template_versions/_details.html 27 mtime=1790765394.491496 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/stacks.template_versions/_d0000664000175000017500000000004415257164522033061 0ustar00zuulzuul{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00118 path=heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/stacks.template_versions/index.html 28 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/stacks.template_versions/in0000664000175000017500000000024115257164522033104 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Template Versions" %}{% endblock %} {% block main %} {{ table.render }} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3122618 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/template_versions/0000775000175000017500000000000015257164606031272 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00114 path=heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/template_versions/_details.html 28 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/template_versions/_details.0000664000175000017500000000004415257164522033052 0ustar00zuulzuul{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00111 path=heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/template_versions/index.html 28 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/templates/template_versions/index.htm0000664000175000017500000000024115257164522033105 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Template Versions" %}{% endblock %} {% block main %} {{ table.render }} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/urls.py0000664000175000017500000000150215257164522025063 0ustar00zuulzuul# 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 django.urls import re_path from heat_dashboard.content.template_versions import views urlpatterns = [ re_path(r'^$', views.TemplateVersionsView.as_view(), name='index'), re_path(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='details'), ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/content/template_versions/views.py0000664000175000017500000000424415257164522025241 0ustar00zuulzuul# 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 django.urls import reverse from django.utils.translation import gettext_lazy as _ from horizon import exceptions from horizon import tables from horizon import tabs from heat_dashboard import api import heat_dashboard.content.template_versions.tables as project_tables import heat_dashboard.content.template_versions.tabs as project_tabs class TemplateVersionsView(tables.DataTableView): table_class = project_tables.TemplateVersionsTable template_name = 'project/template_versions/index.html' page_title = _("Template Versions") def get_data(self): try: template_versions = sorted( api.heat.template_version_list(self.request), key=lambda template_version: template_version.version) except Exception: template_versions = [] msg = _('Unable to retrieve template versions.') exceptions.handle(self.request, msg) return template_versions class DetailView(tabs.TabView): tab_group_class = project_tabs.TemplateVersionDetailsTabs template_name = 'horizon/common/_detail.html' page_title = "{{ template_version }}" def get_template_version(self, request, **kwargs): try: template_functions = api.heat.template_function_list( request, kwargs['template_version']) return template_functions except Exception: msg = _('Unable to retrieve template functions.') exceptions.handle(request, msg, redirect=self.get_redirect_url()) @staticmethod def get_redirect_url(): return reverse('horizon:project:stacks.template_versions:index') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3142617 heat_dashboard-16.0.0/heat_dashboard/enabled/0000775000175000017500000000000015257164606017706 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/enabled/_1610_project_orchestration_panel.py0000664000175000017500000000220015257164522026646 0ustar00zuulzuul# 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 django.utils.translation import gettext_lazy as _ from heat_dashboard import exceptions # The slug of the panel group to be added to HORIZON_CONFIG. Required. PANEL_GROUP = 'orchestration' # The display name of the PANEL_GROUP. Required. PANEL_GROUP_NAME = _('Orchestration') # The slug of the dashboard the PANEL_GROUP associated with. Required. PANEL_GROUP_DASHBOARD = 'project' ADD_INSTALLED_APPS = ["heat_dashboard", ] ADD_EXCEPTIONS = { 'not_found': exceptions.NOT_FOUND, 'recoverable': exceptions.RECOVERABLE, 'unauthorized': exceptions.UNAUTHORIZED } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/enabled/_1620_project_stacks_panel.py0000664000175000017500000000201615257164522025260 0ustar00zuulzuul# 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. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'stacks' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'project' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'orchestration' # Python panel class of the PANEL to be added. ADD_PANEL = 'heat_dashboard.content.stacks.panel.Stacks' # Automatically discover static resources in installed apps AUTO_DISCOVER_STATIC_FILES = True ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/enabled/_1630_project_resource_types_panel.py0000664000175000017500000000217215257164522027047 0ustar00zuulzuul# 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. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'resource_types' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'project' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'orchestration' # Python panel class of the PANEL to be added. ADD_PANEL = 'heat_dashboard.content.resource_types.panel.ResourceTypes' # Automatically discover static resources in installed apps AUTO_DISCOVER_STATIC_FILES = True # ADD_INSTALLED_APPS = ['heat_dashboard.content.resource_types', ] DISABLED = False ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4924963 heat_dashboard-16.0.0/heat_dashboard/enabled/_1640_project_template_versions_panel.py0000664000175000017500000000205615257164522027541 0ustar00zuulzuul# 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. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'template_versions' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'project' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'orchestration' # Python panel class of the PANEL to be added. ADD_PANEL = 'heat_dashboard.content.template_versions.panel.TemplateVersions' # Automatically discover static resources in installed apps AUTO_DISCOVER_STATIC_FILES = True ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4934962 heat_dashboard-16.0.0/heat_dashboard/enabled/_1650_project_template_generator_panel.py0000664000175000017500000000665415257164522027670 0ustar00zuulzuul# 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. import os from horizon.utils.file_discovery import discover_files import heat_dashboard # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'template_generator' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'project' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'orchestration' # Python panel class of the PANEL to be added. ADD_PANEL = 'heat_dashboard.content.template_generator.panel.TemplateGenerator' # Automatically discover static resources in installed apps ADD_ANGULAR_MODULES = \ ['horizon.dashboard.project.heat_dashboard.template_generator'] AUTO_DISCOVER_STATIC_FILES = True TEMPLATE_GENERATOR_BASE = 'dashboard/project/heat_dashboard/template_generator' CSS_BASE = '%s/css' % TEMPLATE_GENERATOR_BASE JS_BASE = '%s/js' % TEMPLATE_GENERATOR_BASE ADD_XSTATIC_MODULES = [ ('xstatic.pkg.angular', [ 'angular-animate.js', 'angular-aria.js', 'angular-messages.js', ]), ('xstatic.pkg.angular_bootstrap', ['angular-bootstrap.js']), # ('xstatic.pkg.angular_material', ['angular-material.js']), ('xstatic.pkg.angular_uuid', ['angular-uuid.js']), ('xstatic.pkg.angular_vis', ['angular-vis.js']), ('xstatic.pkg.filesaver', ['FileSaver.js']), ('xstatic.pkg.js_yaml', ['js-yaml.js']), ('xstatic.pkg.json2yaml', ['json2yaml.js']), ] ADD_SCSS_FILES = [ '%s/hotgen-main.scss' % CSS_BASE ] HEAT_DASHBOARD_ROOT = heat_dashboard.__path__[0] ADD_JS_FILES = discover_files(os.path.join(HEAT_DASHBOARD_ROOT, 'static'), sub_path='%s/libs' % JS_BASE, ext='.js', trim_base_path=True) ADD_JS_FILES.extend([ '%s/components/template-generator.module.js' % JS_BASE, '%s/components/utils.module.js' % JS_BASE, '%s/components/agent.module.js' % JS_BASE, ]) ADD_JS_FILES.extend( [js_file for js_file in discover_files( os.path.join(HEAT_DASHBOARD_ROOT, 'static'), sub_path='%s/components' % JS_BASE, ext='.js', trim_base_path=True) if js_file not in ADD_JS_FILES and not js_file.endswith('spec.js') ]) ADD_JS_FILES.extend( [js_file for js_file in discover_files( os.path.join(HEAT_DASHBOARD_ROOT, 'static'), sub_path='%s/resources' % JS_BASE, ext='.js', trim_base_path=True) if not js_file.endswith('spec.js') ]) ADD_JS_SPEC_FILES = [ js_file for js_file in discover_files( os.path.join(HEAT_DASHBOARD_ROOT, 'static'), sub_path='%s/components' % JS_BASE, ext='.js', trim_base_path=True) if js_file.endswith('spec.js')] ADD_JS_SPEC_FILES.extend( [js_file for js_file in discover_files( os.path.join(HEAT_DASHBOARD_ROOT, 'static'), sub_path='%s/resources' % JS_BASE, ext='.js', trim_base_path=True) if js_file.endswith('spec.js') ]) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4934962 heat_dashboard-16.0.0/heat_dashboard/enabled/__init__.py0000664000175000017500000000000015257164522022002 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4934962 heat_dashboard-16.0.0/heat_dashboard/exceptions.py0000664000175000017500000000136515257164522021051 0ustar00zuulzuul# 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 heatclient import exc as heatclient UNAUTHORIZED = ( heatclient.Unauthorized, ) NOT_FOUND = ( heatclient.NotFound, ) RECOVERABLE = ( heatclient.HTTPException, ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3152616 heat_dashboard-16.0.0/heat_dashboard/local_settings.d/0000775000175000017500000000000015257164606021550 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.494496 heat_dashboard-16.0.0/heat_dashboard/local_settings.d/_1699_orchestration_settings.py0000664000175000017500000000244015257164522027552 0ustar00zuulzuul# 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. # This file is to be included for configuring application which relates # to orchestration(Heat) functions. from django.conf import settings OPENSTACK_HEAT_STACK = { 'enable_user_pass': True, } settings.POLICY_FILES.update({ 'orchestration': 'heat_policy.yaml', }) settings.DEFAULT_POLICY_FILES.update({ 'orchestration': 'default_policies/heat.yaml', }) # Sample # settings.LOGGING['loggers'].update({ # 'heatclient': { # 'handlers': ['console'], # 'level': 'DEBUG', # 'propagate': False, # } # }) # Template Generator retrieve options API TIMEOUT HEAT_TEMPLATE_GENERATOR_API_TIMEOUT = 60 # Template Generator retrieve options API PARALLEL LEVEL HEAT_TEMPLATE_GENERATOR_API_PARALLEL = 2 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.494496 heat_dashboard-16.0.0/heat_dashboard/local_settings.d/__init__.py0000664000175000017500000000000015257164522023644 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2652628 heat_dashboard-16.0.0/heat_dashboard/locale/0000775000175000017500000000000015257164606017553 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.260263 heat_dashboard-16.0.0/heat_dashboard/locale/cs/0000775000175000017500000000000015257164606020160 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3152616 heat_dashboard-16.0.0/heat_dashboard/locale/cs/LC_MESSAGES/0000775000175000017500000000000015257164606021745 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.494496 heat_dashboard-16.0.0/heat_dashboard/locale/cs/LC_MESSAGES/django.po0000664000175000017500000002507315257164522023553 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Daniel ÄŒech , 2016. #zanata # Lenka Husáková , 2016. #zanata # Stanislav Ulrych , 2016. #zanata # ZbynÄ›k Schwarz , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-11-17 09:25+0000\n" "Last-Translator: ZbynÄ›k Schwarz \n" "Language-Team: Czech\n" "Language: cs\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" msgid "A local environment to upload." msgstr "Lokální prostÅ™edí pro nahrání." msgid "A local template to upload." msgstr "Lokální Å¡ablona pro nahrání." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adoptování dokonÄ�eno" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adoptování selhalo" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Probíhá adoptování" msgid "An external (HTTP) URL to load the template from." msgstr "Externí (HTTP) URL z které naÄ�íst Å¡ablonu." msgid "Change Stack Template" msgstr "ZmÄ›nit Å¡ablonu zásobníku" msgid "Change Template" msgstr "ZmÄ›nit Å¡ablonu" msgctxt "current status of stack" msgid "Check Complete" msgstr "Kontrola dokonÄ�ena" msgctxt "current status of stack" msgid "Check Failed" msgstr "Kontrola selhala" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Probíhá kontrola" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Zkontrolovat stack" msgstr[1] "Zkontrolovat stacky" msgstr[2] "Zkontrolovat stacků" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Zásobník zkontrolován" msgstr[1] "Zásobníky zkontrolovány" msgstr[2] "Zásobníků zkontrolováno" msgctxt "current status of stack" msgid "Create Complete" msgstr "VytvoÅ™ení dokonÄ�eno" msgctxt "current status of stack" msgid "Create Failed" msgstr "VytvoÅ™ení selhalo" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Probíhá vytváření" msgid "Create Stack" msgstr "VytvoÅ™it zásobník" msgid "Created" msgstr "VytvoÅ™eno" msgid "Creation Timeout (minutes)" msgstr "ÄŒasový limit vytvoÅ™ení (v minutách)" msgid "Date Updated" msgstr "Nahráno dne" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Smazání dokonÄ�eno" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Smazání selhalo" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Probíhá mazání" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Smazat zásobník" msgstr[1] "Smazat zásobníky" msgstr[2] "Smazat zásobníky" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Zásobník smazán" msgstr[1] "Zásobníky smazány" msgstr[2] "Zásobníků smazáno" msgid "Description" msgstr "Popis" msgid "Direct Input" msgstr "Přímý vstup" msgid "Edit Template" msgstr "Upravit Å¡ablonu" msgid "Enable rollback on create/update failure." msgstr "Povolit zpÄ›tné vrácení pÅ™i chybÄ› ve vytváření/aktualizaci." msgid "Environment Data" msgstr "Data prostÅ™edí" msgid "Environment File" msgstr "Soubor prostÅ™edí" msgid "Environment Source" msgstr "Zdroj prostÅ™edí" msgid "Events" msgstr "Události" msgid "File" msgstr "Soubor" msgid "Function" msgstr "Funkce" msgctxt "current status of stack" msgid "Init Complete" msgstr "Zavedení dokonÄ�eno" msgctxt "current status of stack" msgid "Init Failed" msgstr "Zavedení selhalo" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Probíhá zavádÄ›ní" msgid "Launch" msgstr "Spustit" msgid "Launch Stack" msgstr "Spustit zásobník" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Názvy musí zaÄ�ínat pouze písmenem a může obsahovat pouze písmena, Ä�ísla, " "podtržítka, teÄ�ky a pomlÄ�ky." msgid "Name of the stack to create." msgstr "Název zásobníku pro vytvoÅ™ení." msgid "Next" msgstr "Další" msgid "Orchestration" msgstr "Orchestrace" msgid "Overview" msgstr "PÅ™ehled" #, python-format msgid "Password for user \"%s\"" msgstr "Heslo pro uživatele \"%s\"" #, python-format msgid "Please specify a %s using only one source method." msgstr "Zadejte prosím %s pomocí pouze jedné zdrojové metody." msgid "Preview" msgstr "Náhled" msgid "Preview Stack" msgstr "Náhled zásobníku" msgid "Preview Stack Details" msgstr "Podrobnosti náhledu zásobníku" msgid "Preview Stack Parameters" msgstr "Parametry náhledu zásobníku" msgid "Preview Template" msgstr "Náhled Å¡ablony" msgid "Resource" msgstr "ProstÅ™edek" msgid "Resource Types" msgstr "Typy prostÅ™edků" msgid "Resources" msgstr "ProstÅ™edky" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Obnovení dokonÄ�eno" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Obnovení selhalo" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Probíhá obnovení" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Obnovit stack" msgstr[1] "Obnovit stacky" msgstr[2] "Obnovit stacků" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Zásobník obnoven" msgstr[1] "Zásobníky obnoveny" msgstr[2] "Zásobníků obnoveno" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "ZpÄ›tné vrácení dokonÄ�eno" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "ZpÄ›tné vrácení selhalo" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Probíhá zpÄ›tné vrácení" msgid "Rollback On Failure" msgstr "ZpÄ›tné vrácení pÅ™i selhání" msgid "Select Template" msgstr "Vybrat Å¡ablonu" msgid "Select a new template to preview a stack." msgstr "Vyberte novou Å¡ablonu pro náhled zásobníku." msgid "Select a new template to re-launch a stack." msgstr "Vyberte novou Å¡ablonu pro znovu spuÅ¡tÄ›ní zásobníku." msgid "Select a template to launch a stack." msgstr "Vyberte Å¡ablonu pro spuÅ¡tÄ›ní zásobníku." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Snapshotování dokonÄ�eno" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Snapshotování selhalo" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Probíhá snapshotování" msgid "Stack Events" msgstr "Události zásobníku" msgid "Stack ID" msgstr "Stack ID" msgid "Stack ID =" msgstr "ID zásobníku" msgid "Stack Name" msgstr "Název zásobníku" msgid "Stack Resource" msgstr "Zdroj stack" msgid "Stack Resource Type" msgstr "Typ prostÅ™edku zásobníku" msgid "Stack Resources" msgstr "Zdroje stack" msgid "Stack creation started." msgstr "Zahájeno vytváření zásobníku." msgid "Stack creation timeout in minutes." msgstr "ÄŒasový limit vytvoÅ™ení zásobníku v minutách." msgid "Stack update started." msgstr "Zahájena aktualizace zásobníku." msgid "Stacks" msgstr "Zásobníky" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Stav =" msgid "Status Reason" msgstr "Důvod stavu" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Uspání dokonÄ�eno" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Uspání selhalo" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Probíhá uspání" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Uspat stack" msgstr[1] "Uspat stacky" msgstr[2] "Uspat stacků" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Zásobník uspán" msgstr[1] "Zásobníky uspány" msgstr[2] "Zásobníků uspáno" msgid "Template" msgstr "Å ablona" msgid "Template Data" msgstr "Data Å¡ablony" msgid "Template File" msgstr "Soubor Å¡ablony" msgid "Template Functions" msgstr "Funkce Å¡ablony" msgid "Template Source" msgstr "Zdroj Å¡ablony" msgid "Template URL" msgstr "URL Å¡ablony" msgid "Template Versions" msgstr "Verze Å¡ablon" msgid "The raw contents of the environment file." msgstr "ÄŒistý obsah souboru prostÅ™edí." msgid "The raw contents of the template." msgstr "ÄŒistý obsah Å¡ablony." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "PÅ™i parsování %(prefix)s nastal problém: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Toto je vyžadováno pro operace provádÄ›né bÄ›hem životního cyklu zásobníku" msgid "Time Since Event" msgstr "Doba od události" msgid "Topology" msgstr "Topologie" msgid "Type" msgstr "Typ" msgid "Type =" msgstr "Typ =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Nelze získat události zásobníku \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Nelze získat funkce pro verzi Å¡ablony \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Nelze získat prostÅ™edky zásobníku \"%s\"." msgid "Unable to retrieve metadata." msgstr "Nelze získat metadata." msgid "Unable to retrieve resource type details." msgstr "Nelze získat podrobnosti typu prostÅ™edků." msgid "Unable to retrieve resource." msgstr "Nelze získat prostÅ™edek." msgid "Unable to retrieve stack resource types." msgstr "Nelze získat typy prostÅ™edků zásobníku." msgid "Unable to retrieve stack template." msgstr "Nelze získat Å¡ablonu zásobníku." msgid "Unable to retrieve stack." msgstr "Nelze získat zásobník." msgid "Unable to retrieve template functions." msgstr " Nelze získat funkce Å¡ablony." msgid "Unable to retrieve template versions." msgstr "Nelze získat verze Å¡ablon." msgid "Update" msgstr "Aktualizovat" msgctxt "current status of stack" msgid "Update Complete" msgstr "Aktualizace dokonÄ�ena" msgctxt "current status of stack" msgid "Update Failed" msgstr "Aktualizace selhala." msgctxt "current status of stack" msgid "Update In Progress" msgstr "Probíhá aktualizace" msgid "Update Stack" msgstr "Aktualizovat zásobník" msgid "Update Stack Parameters" msgstr "Aktualizovat parametry zásobníku" msgid "Updated" msgstr "Aktualizováno" msgid "Version" msgstr "Verze" msgid "You must specify a template via one of the available sources." msgstr "Musíte zadat Å¡ablonu pomocí jednoho z dostupných zdrojů." msgid "environment" msgstr "prostÅ™edí" msgid "template" msgstr "Å¡ablona" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.260263 heat_dashboard-16.0.0/heat_dashboard/locale/de/0000775000175000017500000000000015257164606020143 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3162618 heat_dashboard-16.0.0/heat_dashboard/locale/de/LC_MESSAGES/0000775000175000017500000000000015257164606021730 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4954963 heat_dashboard-16.0.0/heat_dashboard/locale/de/LC_MESSAGES/django.po0000664000175000017500000003503315257164522023533 0ustar00zuulzuul# Eugen Block , 2016. #zanata # Frank Kloeker , 2016. #zanata # Robert Simai , 2016. #zanata # Andreas Jaeger , 2017. #zanata # Frank Kloeker , 2017. #zanata # Andreas Jaeger , 2018. #zanata # Frank Kloeker , 2018. #zanata # Robert Simai , 2018. #zanata # Andreas Jaeger , 2020. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2020-10-05 06:22+0000\n" "Last-Translator: Andreas Jaeger \n" "Language-Team: German\n" "Language: de\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "Eine lokale Umgebung zum Hochladen." msgid "A local template to upload." msgstr "Eine lokale Vorlage zum Hochladen." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "Eine Vorlage wird benutzt, um die Bereitstellung von Infrastruktur, Dienste " "und Anwendungen zu automatisieren." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Übernahme abgeschlossen" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Übernahme fehlgeschlagen" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Übernahme im Gang" msgid "An external (HTTP) URL to load the template from." msgstr "Eine externe (HTTP) URL, von der die Vorlage geladen wird." msgid "Attributes" msgstr "Attribute" msgid "Case sensitive" msgstr "Groß- und Kleinschreibung beachten" msgid "Case-sensitive" msgstr "Groß- und Kleinschreibung beachten" msgid "Change Stack Template" msgstr "Ändern der Stack-Vorlage" msgid "Change Template" msgstr "Vorlage wechseln" msgctxt "current status of stack" msgid "Check Complete" msgstr "Überprüfung abgeschlossen" msgctxt "current status of stack" msgid "Check Failed" msgstr "Überprüfung fehlgeschlagen" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Überprüfung im Gang" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Stack überprüfen" msgstr[1] "Stacks überprüfen" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Geprüfter Stack" msgstr[1] "Geprüfte Stacks" msgid "Clear Canvas" msgstr "Canvas leeren" msgid "Close" msgstr "Schliessen" msgctxt "current status of stack" msgid "Create Complete" msgstr "Erstellen abgeschlossen" msgctxt "current status of stack" msgid "Create Failed" msgstr "Erstellen fehlgeschlagen" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Erstellung im Gang" msgid "Create Stack" msgstr "Erzeuge Stack" msgid "Create a new stack with the provided values." msgstr "Erzeuge einen neuen Stack mit den eingegebenen Werten." msgid "Created" msgstr "Erstellt" msgid "Creation Timeout (minutes)" msgstr "Zeitüberschreitung bei der Erzeugung (Minuten)" msgid "Date Updated" msgstr "Datum aktualisiert" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Löschen abgeschlossen" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Löschvorgang fehlgeschlagen" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Löschen im Gang" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Stack löschen" msgstr[1] "Stacks löschen" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Stack gelöscht" msgstr[1] "Stacks gelöscht" msgid "Description" msgstr "Beschreibung" msgid "Description:" msgstr "Beschreibung:" msgid "Direct Input" msgstr "Direkteingabe" msgid "Disabled" msgstr "Inaktiviert" msgid "Edit Template" msgstr "Vorlage editieren" msgid "Enable rollback on create/update failure." msgstr "" "Ermögliche ein Zurückrollen nach einem Erzeugungs/Aktualisierungs-Fehler." msgid "Enabled" msgstr "Aktiviert" msgid "Environment Data" msgstr "Umgebungsdaten" msgid "Environment File" msgstr "Umgebungsdatei" msgid "Environment Source" msgstr "Umgebungsquelle" msgid "Events" msgstr "Veranstaltungen" msgid "Export Draft" msgstr "Entwurf exportieren" msgid "File" msgstr "Datei" msgid "Function" msgstr "Funktion" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "Entwurf importieren" msgctxt "current status of stack" msgid "Init Complete" msgstr "Initialisierung abgeschlossen" msgctxt "current status of stack" msgid "Init Failed" msgstr " Initialisierung fehlgeschlagen" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Initialisierung im Gang" msgid "Last Updated" msgstr "Letzte Aktualisierung" msgid "Launch" msgstr "Starten" msgid "Launch Parameters" msgstr "Startparameter" msgid "Launch Stack" msgstr "Stack starten" msgid "Links" msgstr "Links" msgid "Load Draft" msgstr "Entwurf laden" msgid "Manage Drafts" msgstr "Entwürfe verwalten" msgid "Minutes" msgstr "Minuten" msgid "Name" msgstr "Name" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Der Name muss mit einem Buchstaben beginnen und darf nur Buchstaben, " "Nummern, Unterstriche, Punkte und Bindestriche enthalten." msgid "Name of the stack to create." msgstr "Name des Stacks, der erzeugt werden soll." msgid "Next" msgstr "Weiter" msgid "No images available" msgstr "Keine Abbilder verfügbar" msgid "Orchestration" msgstr "Orchestrierung" msgid "Outputs" msgstr "Ausgaben" msgid "Overview" msgstr "Übersicht" msgid "Parameters" msgstr "Parameter" #, python-format msgid "Password for user \"%s\"" msgstr "Passwort für Benutzer \"%s\"" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "Bitte ziehen Sie Icons in das Bildfenster, um Vorlageresourcen zu definieren." #, python-format msgid "Please specify a %s using only one source method." msgstr "" "Bitte spezifizieren Sie ein %s, indem Sie nur eine Quell-Methode benutzen." msgid "Preview" msgstr "Vorschau" msgid "Preview Stack" msgstr "Stack Vorschau" msgid "Preview Stack Details" msgstr "Vorschau Stack Details" msgid "Preview Stack Parameters" msgstr "Stack Parameter Vorschau" msgid "Preview Template" msgstr "Vorschau Vorlage" msgid "Preview a new stack with the provided values." msgstr "Vorschau eines neuen Stacks mit den angegebenen Werten." msgid "Properties" msgstr "Eigenschaften" msgid "Referenced Files" msgstr "Referenzierte Datei" msgid "Resource" msgstr "Resource" msgid "Resource ID" msgstr "Ressource ID" msgid "Resource Metadata" msgstr "Ressource Metadaten" msgid "Resource Types" msgstr "Ressourcentypen" msgid "Resources" msgstr "Ressourcen" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Wiederaufnahme abgeschlossen" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Wiederaufnahme fehlgeschlagen" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Wiederaufnehmen im Gang" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Stack wiederaufnehmen" msgstr[1] "Stacks wiederaufnehmen" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Wiederaufgenommener Stack" msgstr[1] "Wiederaufgenommene Stacks" msgid "Rollback" msgstr "Rollback" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Wiederholen abgeschlossen" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Wiederholen fehlgeschlagen" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Wiederholung im Gang" msgid "Rollback On Failure" msgstr "Zurückrollen nach einem Fehler" msgid "Save Draft" msgstr "Entwurf speichern" msgid "Select Image" msgstr "Abbild auswählen" msgid "Select Template" msgstr "Vorlage auswählen" msgid "Select a new template to preview a stack." msgstr "Wählen Sie eine neue Vorlage für die Stackvorschau." msgid "Select a new template to re-launch a stack." msgstr "Eine neue Vorlage auswählen, um einen Stack erneut zu starten." msgid "Select a template to launch a stack." msgstr "Vorlage auswählen, um einen Stack zu starten." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Schattenkopie abgeschlossen" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Schattenkopie fehlgeschlagen" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Schattenkopie im Gang" msgid "Stack Events" msgstr "Stackereignisse" msgid "Stack ID" msgstr "Stack ID" msgid "Stack ID =" msgstr "Stack ID =" msgid "Stack Name" msgstr "Stack Name" msgid "Stack Name =" msgstr "Stack Name =" msgid "Stack Parameters" msgstr "Stack Parameter" msgid "Stack Preview" msgstr "Stack Vorschau" msgid "Stack Resource" msgstr "Stackressource" msgid "Stack Resource ID" msgstr "Stack Resource ID" msgid "Stack Resource Type" msgstr "Stack-Ressourcentyp" msgid "Stack Resources" msgstr "Stackressourcen" msgid "Stack creation started." msgstr "Erzeugung des Stacks gestartet." msgid "Stack creation timeout in minutes." msgstr "Zeitüberschreitung bei der Erzeugung in Minuten." msgid "Stack update started." msgstr "Stack-Aktualisierung gestartet." msgid "Stack updating timeout in minutes." msgstr "Stack-Aktualisierungs-Zeitlimit in Minuten." msgid "Stacks" msgstr "Stacks" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Status" msgid "Status Reason" msgstr "Status Grund" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Aussetzen abgeschlossen" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Aussetzen fehlgeschlagen" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Aussetzen läuft" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Stack aussetzen" msgstr[1] "Stacks aussetzen" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Ausgesetzter Stack" msgstr[1] "Ausgesetzte Stacks" msgid "Template" msgstr "Vorlage" msgid "Template Data" msgstr "Vorlage Daten" msgid "Template File" msgstr "Vorlagendatei" msgid "Template Functions" msgstr "Vorlagenfunktionen" msgid "Template Generator" msgstr "Vorlagengenerator" msgid "Template Source" msgstr "Vorlagenquelle" msgid "Template URL" msgstr "Vorlage URL" msgid "Template Version" msgstr "Vorlagenversion" msgid "Template Versions" msgstr "Vorlagenversionen" msgid "The raw contents of the environment file." msgstr "Der rohe Inhalt der Umgebungsdatei." msgid "The raw contents of the template." msgstr "Der rohe Inhalt der Vorlage" #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "Es gab ein Problem beim Analysieren von %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Dies wird zur Ausführung von Operationen während des Lebenszykluses des " "Stacks benötigt." msgid "Time Since Event" msgstr "Zeit seit Ereignis" msgid "Timeout" msgstr "Zeitlimit" msgid "Topology" msgstr "Topologie" msgid "Type" msgstr "Typ" msgid "Type =" msgstr "Typ =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Außerstande Ereignisse für Stack \"%s\" zu bekommen." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "" "Funktionen für die Vorlagenversion \"%s\" können nicht abgerufen werden." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Außerstande Ressourcen für Stack \"%s\" zu bekommen." msgid "Unable to retrieve images" msgstr "Abbilder können nicht abgerufen werden" msgid "Unable to retrieve metadata." msgstr "Metadaten können nicht abgerufen werden." msgid "Unable to retrieve resource type details." msgstr "Ressourcentyp-Details können nicht abgerufen werden." msgid "Unable to retrieve resource." msgstr "Ressource kann nicht abgerufen werden." msgid "Unable to retrieve stack resource types." msgstr "Stack-Ressourcetypen können nicht abgerufen werden." msgid "Unable to retrieve stack template." msgstr "Stackvorlage kann nicht abgerufen werden." msgid "Unable to retrieve stack." msgstr "Stack kann nicht abgerufen werden." msgid "Unable to retrieve template functions." msgstr "Vorlagenfunktionen können nicht abgerufen werden." msgid "Unable to retrieve template versions." msgstr "Vorlagenversionen können nicht abgerufen werden." msgid "Update" msgstr "Aktualisieren" msgctxt "current status of stack" msgid "Update Complete" msgstr "Aktualisierung abgeschlossen" msgctxt "current status of stack" msgid "Update Failed" msgstr "Aktualisierung fehlgeschlagen" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Aktualisierung im Gang" msgid "Update Stack" msgstr "Stack aktualisieren" msgid "Update Stack Parameters" msgstr "Stack-Parameter aktualisieren" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "Stack mit den angegebenen Werten aktualisieren. Bitte beachten Sie, dass " "sämtliche verschlüsselten Parameter, wie z.B. Passwörter, auf ihre " "Standardwerte zurückgesetzt werden, wenn Sie sie hier nicht ändern." msgid "Updated" msgstr "Aktualisiert" msgid "Updating Timeout (minutes)" msgstr "Aktualisierungs-Zeitlimit (Minuten)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "Verwenden Sie eine der verfügbaren Vorlagenquellenoptionen, um die Vorlage " "zur Änderung des Stacks anzugeben." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "Nutzen Sie eine der verfügbaren Vorlagenquellenoptionen, um die Vorlage zur " "Erzeugung dieses Stacks anzugeben." msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "Verwenden Sie eine der verfügbaren Vorlagenquellenoptionen, um die Vorlage " "zur Vorschau des Stacks anzugeben," msgid "Version" msgstr "Version" msgid "You must specify a template via one of the available sources." msgstr "Sie müssen eine Vorlage aus den vorhandenen Quellen angeben." msgid "environment" msgstr "Umgebung" msgid "template" msgstr "Vorlage" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4964962 heat_dashboard-16.0.0/heat_dashboard/locale/de/LC_MESSAGES/djangojs.po0000664000175000017500000000626315257164522024073 0ustar00zuulzuul# Frank Kloeker , 2016. #zanata # Andreas Jaeger , 2017. #zanata # Frank Kloeker , 2017. #zanata # Robert Simai , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-08-14 00:21+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-08-09 11:46+0000\n" "Last-Translator: Robert Simai \n" "Language-Team: German\n" "Language: de\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "Cancel" msgstr "Abbrechen" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Stack überprüfen" msgstr[1] "Stacks überprüfen" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Geprüfter Stack: %s." msgstr[1] "Geprüfte Stacks: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Stack prüfen bestätigen" msgstr[1] "Stacks prüfen bestätigen" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Stack löschen bestätigen" msgstr[1] "Stacks löschen bestätigen" msgid "Create Stack" msgstr "Erzeuge Stack" msgid "Delete Resource" msgstr "Lösche Ressource" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Stack löschen" msgstr[1] "Stacks löschen" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Gelöschter Stack: %s." msgstr[1] "Gelöschte Stacks: %s." msgid "Download" msgstr "Herunterladen" msgid "Heat Orchestration Template" msgstr "Heat Orchestration Template" msgid "OK" msgstr "OK" msgid "Save" msgstr "Speichern" msgid "Saved Drafts" msgstr "Gespeicherte Vorlagen" msgid "Show More Properties" msgstr "Zeige mehr Eigenschaften" #, python-format msgid "Stack %s was successfully created." msgstr "Stack %s wurde erfolgreich erstellt." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Stack kann nicht geprüft werden: %s." msgstr[1] "Stacks können nicht geprüft werden: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Stack kann nicht gelöscht werden: %s." msgstr[1] "Stacks können nicht gelöscht werden: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "Sie haben keine Berechtigung zum prüfen von Stacks: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "Sie haben keine Berechtigung zum löschen von Stacks: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "Sie haben \"%s\" ausgewählt." msgstr[1] "Sie haben \"%s\" ausgewählt." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "" "Sie haben \"%s\" gewählt. Gelöschter Stack kann nicht wiederhergestellt " "werden." msgstr[1] "" "Sie haben \"%s\" gewählt. Gelöschte Stacks können nicht wiederhergestellt " "werden." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.261263 heat_dashboard-16.0.0/heat_dashboard/locale/en_AU/0000775000175000017500000000000015257164606020542 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3162618 heat_dashboard-16.0.0/heat_dashboard/locale/en_AU/LC_MESSAGES/0000775000175000017500000000000015257164606022327 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4964962 heat_dashboard-16.0.0/heat_dashboard/locale/en_AU/LC_MESSAGES/django.po0000664000175000017500000002356515257164522024141 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Tom Fifield , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Tom Fifield , 2016. #zanata # Tom Fifield , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-08-21 06:42+0000\n" "Last-Translator: Tom Fifield \n" "Language-Team: English (Australia)\n" "Language: en_AU\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "A local environment to upload." msgstr "A local environment to upload." msgid "A local template to upload." msgstr "A local template to upload." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adopt Complete" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adopt Failed" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adopt In Progress" msgid "An external (HTTP) URL to load the template from." msgstr "An external (HTTP) URL to load the template from." msgid "Case sensitive" msgstr "Case sensitive" msgid "Case-sensitive" msgstr "Case-sensitive" msgid "Change Stack Template" msgstr "Change Stack Template" msgid "Change Template" msgstr "Change Template" msgctxt "current status of stack" msgid "Check Complete" msgstr "Check Complete" msgctxt "current status of stack" msgid "Check Failed" msgstr "Check Failed" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Check In Progress" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Check Stack" msgstr[1] "Check Stacks" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Checked Stack" msgstr[1] "Checked Stacks" msgctxt "current status of stack" msgid "Create Complete" msgstr "Create Complete" msgctxt "current status of stack" msgid "Create Failed" msgstr "Create Failed" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Create In Progress" msgid "Create Stack" msgstr "Create Stack" msgid "Created" msgstr "Created" msgid "Creation Timeout (minutes)" msgstr "Creation Timeout (minutes)" msgid "Date Updated" msgstr "Date Updated" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Delete Complete" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Delete Failed" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Delete In Progress" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Delete Stack" msgstr[1] "Delete Stacks" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Deleted Stack" msgstr[1] "Deleted Stacks" msgid "Description" msgstr "Description" msgid "Direct Input" msgstr "Direct Input" msgid "Edit Template" msgstr "Edit Template" msgid "Enable rollback on create/update failure." msgstr "Enable rollback on create/update failure." msgid "Environment Data" msgstr "Environment Data" msgid "Environment File" msgstr "Environment File" msgid "Environment Source" msgstr "Environment Source" msgid "Events" msgstr "Events" msgid "File" msgstr "File" msgid "Function" msgstr "Function" msgctxt "current status of stack" msgid "Init Complete" msgstr "Init Complete" msgctxt "current status of stack" msgid "Init Failed" msgstr "Init Failed" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Init In Progress" msgid "Launch" msgstr "Launch" msgid "Launch Stack" msgstr "Launch Stack" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Name must start with a letter and may only contain letters, numbers, " "underscores, full stops and hyphens." msgid "Name of the stack to create." msgstr "Name of the stack to create." msgid "Next" msgstr "Next" msgid "Orchestration" msgstr "Orchestration" msgid "Overview" msgstr "Overview" #, python-format msgid "Password for user \"%s\"" msgstr "Password for user \"%s\"" #, python-format msgid "Please specify a %s using only one source method." msgstr "Please specify a %s using only one source method." msgid "Preview" msgstr "Preview" msgid "Preview Stack" msgstr "Preview Stack" msgid "Preview Stack Details" msgstr "Preview Stack Details" msgid "Preview Stack Parameters" msgstr "Preview Stack Parameters" msgid "Preview Template" msgstr "Preview Template" msgid "Resource" msgstr "Resource" msgid "Resource Types" msgstr "Resource Types" msgid "Resources" msgstr "Resources" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Resume Complete" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Resume Failed" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Resume In Progress" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Resume Stack" msgstr[1] "Resume Stacks" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Resumed Stack" msgstr[1] "Resumed Stacks" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Rollback Complete" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Rollback Failed" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Rollback In Progress" msgid "Rollback On Failure" msgstr "Rollback On Failure" msgid "Select Template" msgstr "Select Template" msgid "Select a new template to preview a stack." msgstr "Select a new template to preview a stack." msgid "Select a new template to re-launch a stack." msgstr "Select a new template to re-launch a stack." msgid "Select a template to launch a stack." msgstr "Select a template to launch a stack." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Snapshot Complete" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Snapshot Failed" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Snapshot In Progress" msgid "Stack Events" msgstr "Stack Events" msgid "Stack ID" msgstr "Stack ID" msgid "Stack ID =" msgstr "Stack ID =" msgid "Stack Name" msgstr "Stack Name" msgid "Stack Name =" msgstr "Stack Name =" msgid "Stack Resource" msgstr "Stack Resource" msgid "Stack Resource Type" msgstr "Stack Resource Type" msgid "Stack Resources" msgstr "Stack Resources" msgid "Stack creation started." msgstr "Stack creation started." msgid "Stack creation timeout in minutes." msgstr "Stack creation timeout in minutes." msgid "Stack update started." msgstr "Stack update started." msgid "Stacks" msgstr "Stacks" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Status =" msgid "Status Reason" msgstr "Status Reason" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspend Complete" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspend Failed" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspend In Progress" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Suspend Stack" msgstr[1] "Suspend Stacks" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Suspended Stack" msgstr[1] "Suspended Stacks" msgid "Template" msgstr "Template" msgid "Template Data" msgstr "Template Data" msgid "Template File" msgstr "Template File" msgid "Template Functions" msgstr "Template Functions" msgid "Template Source" msgstr "Template Source" msgid "Template URL" msgstr "Template URL" msgid "Template Versions" msgstr "Template Versions" msgid "The raw contents of the environment file." msgstr "The raw contents of the environment file." msgid "The raw contents of the template." msgstr "The raw contents of the template." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "There was a problem parsing the %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgid "Time Since Event" msgstr "Time Since Event" msgid "Topology" msgstr "Topology" msgid "Type" msgstr "Type" msgid "Type =" msgstr "Type =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Unable to get events for stack \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Unable to get functions for template version \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Unable to get resources for stack \"%s\"." msgid "Unable to retrieve metadata." msgstr "Unable to retrieve metadata." msgid "Unable to retrieve resource type details." msgstr "Unable to retrieve resource type details." msgid "Unable to retrieve resource." msgstr "Unable to retrieve resource." msgid "Unable to retrieve stack resource types." msgstr "Unable to retrieve stack resource types." msgid "Unable to retrieve stack template." msgstr "Unable to retrieve stack template." msgid "Unable to retrieve stack." msgstr "Unable to retrieve stack." msgid "Unable to retrieve template functions." msgstr "Unable to retrieve template functions." msgid "Unable to retrieve template versions." msgstr "Unable to retrieve template versions." msgid "Update" msgstr "Update" msgctxt "current status of stack" msgid "Update Complete" msgstr "Update Complete" msgctxt "current status of stack" msgid "Update Failed" msgstr "Update Failed" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Update In Progress" msgid "Update Stack" msgstr "Update Stack" msgid "Update Stack Parameters" msgstr "Update Stack Parameters" msgid "Updated" msgstr "Updated" msgid "Version" msgstr "Version" msgid "You must specify a template via one of the available sources." msgstr "You must specify a template via one of the available sources." msgid "environment" msgstr "environment" msgid "template" msgstr "template" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.261263 heat_dashboard-16.0.0/heat_dashboard/locale/en_GB/0000775000175000017500000000000015257164606020525 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3172617 heat_dashboard-16.0.0/heat_dashboard/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000015257164606022312 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4974961 heat_dashboard-16.0.0/heat_dashboard/locale/en_GB/LC_MESSAGES/django.po0000664000175000017500000003324115257164522024114 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Rob Cresswell , 2015. #zanata # Andi Chandler , 2016. #zanata # Andreas Jaeger , 2016. #zanata # Andi Chandler , 2017. #zanata # Julie Pichon , 2017. #zanata # Andi Chandler , 2018. #zanata # Andi Chandler , 2020. #zanata # Andi Chandler , 2026. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-11-20 17:42+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2026-01-07 11:20+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "A local environment to upload." msgid "A local template to upload." msgstr "A local template to upload." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adopt Complete" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adopt Failed" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adopt In Progress" msgid "An external (HTTP) URL to load the template from." msgstr "An external (HTTP) URL to load the template from." msgid "Attributes" msgstr "Attributes" msgid "Case sensitive" msgstr "Case sensitive" msgid "Case-sensitive" msgstr "Case-sensitive" msgid "Change Stack Template" msgstr "Change Stack Template" msgid "Change Template" msgstr "Change Template" msgctxt "current status of stack" msgid "Check Complete" msgstr "Check Complete" msgctxt "current status of stack" msgid "Check Failed" msgstr "Check Failed" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Check In Progress" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Check Stack" msgstr[1] "Check Stacks" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Checked Stack" msgstr[1] "Checked Stacks" msgid "Clear Canvas" msgstr "Clear Canvas" msgid "Close" msgstr "Close" msgctxt "current status of stack" msgid "Create Complete" msgstr "Create Complete" msgctxt "current status of stack" msgid "Create Failed" msgstr "Create Failed" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Create In Progress" msgid "Create Stack" msgstr "Create Stack" msgid "Create a new stack with the provided values." msgstr "Create a new stack with the provided values." msgid "Created" msgstr "Created" msgid "Creation Timeout (minutes)" msgstr "Creation Timeout (minutes)" msgid "Date Updated" msgstr "Date Updated" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Delete Complete" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Delete Failed" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Delete In Progress" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Delete Stack" msgstr[1] "Delete Stacks" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Deleted Stack" msgstr[1] "Deleted Stacks" msgid "Description" msgstr "Description" msgid "Description:" msgstr "Description:" msgid "Direct Input" msgstr "Direct Input" msgid "Disabled" msgstr "Disabled" msgid "Edit Template" msgstr "Edit Template" msgid "Enable rollback on create/update failure." msgstr "Enable rollback on create/update failure." msgid "Enabled" msgstr "Enabled" msgid "Environment Data" msgstr "Environment Data" msgid "Environment File" msgstr "Environment File" msgid "Environment Source" msgstr "Environment Source" msgid "Events" msgstr "Events" msgid "Export Draft" msgstr "Export Draft" msgid "File" msgstr "File" msgid "Function" msgstr "Function" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "Import Draft" msgctxt "current status of stack" msgid "Init Complete" msgstr "Init Complete" msgctxt "current status of stack" msgid "Init Failed" msgstr "Init Failed" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Init In Progress" msgid "Last Updated" msgstr "Last Updated" msgid "Launch" msgstr "Launch" msgid "Launch Parameters" msgstr "Launch Parameters" msgid "Launch Stack" msgstr "Launch Stack" msgid "Links" msgstr "Links" msgid "Load Draft" msgstr "Load Draft" msgid "Manage Drafts" msgstr "Manage Drafts" msgid "Minutes" msgstr "Minutes" msgid "Name" msgstr "Name" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Name must start with a letter and may only contain letters, numbers, " "underscores, full stops and hyphens." msgid "Name of the stack to create." msgstr "Name of the stack to create." msgid "Next" msgstr "Next" msgid "No images available" msgstr "No images available" msgid "Orchestration" msgstr "Orchestration" msgid "Outputs" msgstr "Outputs" msgid "Overview" msgstr "Overview" msgid "Parameters" msgstr "Parameters" #, python-format msgid "Password for user \"%s\"" msgstr "Password for user \"%s\"" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "Please drag and drop resource icons into centre canvas to define resource " "for template." #, python-format msgid "Please specify a %s using only one source method." msgstr "Please specify a %s using only one source method." msgid "Preview" msgstr "Preview" msgid "Preview Stack" msgstr "Preview Stack" msgid "Preview Stack Details" msgstr "Preview Stack Details" msgid "Preview Stack Parameters" msgstr "Preview Stack Parameters" msgid "Preview Template" msgstr "Preview Template" msgid "Preview a new stack with the provided values." msgstr "Preview a new stack with the provided values." msgid "Properties" msgstr "Properties" msgid "Referenced Files" msgstr "Referenced Files" msgid "Resource" msgstr "Resource" msgid "Resource ID" msgstr "Resource ID" msgid "Resource Metadata" msgstr "Resource Metadata" msgid "Resource Types" msgstr "Resource Types" msgid "Resources" msgstr "Resources" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Resume Complete" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Resume Failed" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Resume In Progress" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Resume Stack" msgstr[1] "Resume Stacks" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Resumed Stack" msgstr[1] "Resumed Stacks" msgid "Rollback" msgstr "Rollback" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Rollback Complete" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Rollback Failed" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Rollback In Progress" msgid "Rollback On Failure" msgstr "Rollback On Failure" msgid "Save Draft" msgstr "Save Draft" msgid "Select Image" msgstr "Select Image" msgid "Select Template" msgstr "Select Template" msgid "Select a new template to preview a stack." msgstr "Select a new template to preview a stack." msgid "Select a new template to re-launch a stack." msgstr "Select a new template to re-launch a stack." msgid "Select a template to launch a stack." msgstr "Select a template to launch a stack." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Snapshot Complete" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Snapshot Failed" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Snapshot In Progress" msgid "Stack Events" msgstr "Stack Events" msgid "Stack ID" msgstr "Stack ID" msgid "Stack ID =" msgstr "Stack ID =" msgid "Stack Name" msgstr "Stack Name" msgid "Stack Name =" msgstr "Stack Name =" msgid "Stack Parameters" msgstr "Stack Parameters" msgid "Stack Preview" msgstr "Stack Preview" msgid "Stack Resource" msgstr "Stack Resource" msgid "Stack Resource ID" msgstr "Stack Resource ID" msgid "Stack Resource Type" msgstr "Stack Resource Type" msgid "Stack Resources" msgstr "Stack Resources" msgid "Stack creation started." msgstr "Stack creation started." msgid "Stack creation timeout in minutes." msgstr "Stack creation timeout in minutes." msgid "Stack update started." msgstr "Stack update started." msgid "Stack updating timeout in minutes." msgstr "Stack updating timeout in minutes." msgid "Stacks" msgstr "Stacks" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Status =" msgid "Status Reason" msgstr "Status Reason" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspend Complete" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspend Failed" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspend In Progress" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Suspend Stack" msgstr[1] "Suspend Stacks" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Suspended Stack" msgstr[1] "Suspended Stacks" msgid "Template" msgstr "Template" msgid "Template Data" msgstr "Template Data" msgid "Template File" msgstr "Template File" msgid "Template Functions" msgstr "Template Functions" msgid "Template Generator" msgstr "Template Generator" msgid "Template Source" msgstr "Template Source" msgid "Template URL" msgstr "Template URL" msgid "Template Version" msgstr "Template Version" msgid "Template Versions" msgstr "Template Versions" msgid "The raw contents of the environment file." msgstr "The raw contents of the environment file." msgid "The raw contents of the template." msgstr "The raw contents of the template." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "There was a problem parsing the %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgid "Time Since Event" msgstr "Time Since Event" msgid "Timeout" msgstr "Timeout" msgid "Topology" msgstr "Topology" msgid "Type" msgstr "Type" msgid "Type =" msgstr "Type =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Unable to get events for stack \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Unable to get functions for template version \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Unable to get resources for stack \"%s\"." msgid "Unable to retrieve images" msgstr "Unable to retrieve images" msgid "Unable to retrieve metadata." msgstr "Unable to retrieve metadata." msgid "Unable to retrieve resource type details." msgstr "Unable to retrieve resource type details." msgid "Unable to retrieve resource." msgstr "Unable to retrieve resource." msgid "Unable to retrieve stack resource types." msgstr "Unable to retrieve stack resource types." msgid "Unable to retrieve stack template." msgstr "Unable to retrieve stack template." msgid "Unable to retrieve stack." msgstr "Unable to retrieve stack." msgid "Unable to retrieve stacks list." msgstr "Unable to retrieve stacks list." msgid "Unable to retrieve template functions." msgstr "Unable to retrieve template functions." msgid "Unable to retrieve template versions." msgstr "Unable to retrieve template versions." msgid "Update" msgstr "Update" msgctxt "current status of stack" msgid "Update Complete" msgstr "Update Complete" msgctxt "current status of stack" msgid "Update Failed" msgstr "Update Failed" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Update In Progress" msgid "Update Stack" msgstr "Update Stack" msgid "Update Stack Parameters" msgstr "Update Stack Parameters" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgid "Updated" msgstr "Updated" msgid "Updating Timeout (minutes)" msgstr "Updating Timeout (minutes)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgid "Version" msgstr "Version" msgid "You must specify a template via one of the available sources." msgstr "You must specify a template via one of the available sources." msgid "environment" msgstr "environment" msgid "template" msgstr "template" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4974961 heat_dashboard-16.0.0/heat_dashboard/locale/en_GB/LC_MESSAGES/djangojs.po0000664000175000017500000000565315257164522024457 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Andi Chandler , 2017. #zanata # Andi Chandler , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-06-08 03:05+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-06-09 11:33+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "Cancel" msgstr "Cancel" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Check Stack" msgstr[1] "Check Stacks" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Checked Stack: %s." msgstr[1] "Checked Stacks: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Confirm Check Stack" msgstr[1] "Confirm Check Stacks" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Confirm Delete Stack" msgstr[1] "Confirm Delete Stacks" msgid "Create Stack" msgstr "Create Stack" msgid "Delete Resource" msgstr "Delete Resource" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Delete Stack" msgstr[1] "Delete Stacks" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Deleted Stack: %s." msgstr[1] "Deleted Stacks: %s." msgid "Download" msgstr "Download" msgid "Heat Orchestration Template" msgstr "Heat Orchestration Template" msgid "OK" msgstr "OK" msgid "Save" msgstr "Save" msgid "Saved Drafts" msgstr "Saved Drafts" msgid "Show More Properties" msgstr "Show More Properties" #, python-format msgid "Stack %s was successfully created." msgstr "Stack %s was successfully created." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Unable to check Stack: %s." msgstr[1] "Unable to check Stacks: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Unable to delete Stack: %s." msgstr[1] "Unable to delete Stacks: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "You are not allowed to check stacks: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "You are not allowed to delete stacks: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "You have selected \"%s\"." msgstr[1] "You have selected \"%s\"." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "You have selected \"%s\". Deleted stack is not recoverable." msgstr[1] "You have selected \"%s\". Deleted stacks are not recoverable." ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2622628 heat_dashboard-16.0.0/heat_dashboard/locale/es/0000775000175000017500000000000015257164606020162 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3172617 heat_dashboard-16.0.0/heat_dashboard/locale/es/LC_MESSAGES/0000775000175000017500000000000015257164606021747 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.498496 heat_dashboard-16.0.0/heat_dashboard/locale/es/LC_MESSAGES/django.po0000664000175000017500000002365315257164522023557 0ustar00zuulzuul# Eddie Ramirez , 2016. #zanata # Eugènia Torrella , 2016. #zanata # Jose Porrua , 2016. #zanata # Carlos Camacho , 2017. #zanata # Zeus Arias Lucero , 2017. #zanata # Alberto Molina Coballes , 2021. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2021-06-07 07:10+0000\n" "Last-Translator: Alberto Molina Coballes \n" "Language-Team: Spanish\n" "Language: es\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "A local environment to upload." msgstr "Entorno local a cargar" msgid "A local template to upload." msgstr "Una plantilla local a cargar." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adoptar completado" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adoptar fallido" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adoptar en curso" msgid "An external (HTTP) URL to load the template from." msgstr "Una URL externa (HTTP) desde la que cargar la plantilla." msgid "Change Stack Template" msgstr "Cambiar plantilla de la pila" msgid "Change Template" msgstr "Cambiar plantilla" msgctxt "current status of stack" msgid "Check Complete" msgstr "Marcar completado" msgctxt "current status of stack" msgid "Check Failed" msgstr "Marcar fallido" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Verificar en curso" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Comprobar pila" msgstr[1] "Comprobar pilas" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Pila comprobada" msgstr[1] "Pilas comprobadas" msgctxt "current status of stack" msgid "Create Complete" msgstr "Creación completada" msgctxt "current status of stack" msgid "Create Failed" msgstr "Crear fallido" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Crear en curso" msgid "Create Stack" msgstr "Crear pila" msgid "Created" msgstr "Creado" msgid "Creation Timeout (minutes)" msgstr "Tiempo de espera de creación (minutos)" msgid "Date Updated" msgstr "Fecha actualizada" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Suprimir completado" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Suprimir fallido" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Suprimir en curso" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Suprimir pila" msgstr[1] "Suprimir pilas" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Pila suprimida" msgstr[1] "Pilas suprimidas" msgid "Description" msgstr "Descripción " msgid "Direct Input" msgstr "Entrada directa" msgid "Edit Template" msgstr "Editar plantilla" msgid "Enable rollback on create/update failure." msgstr "Habilitar restauración tras fallo de creación/actualización." msgid "Environment Data" msgstr "Datos del entorno" msgid "Environment File" msgstr "Archivo de entorno" msgid "Environment Source" msgstr "Origen de entorno" msgid "Events" msgstr "Sucesos" msgid "File" msgstr "Archivo" msgctxt "current status of stack" msgid "Init Complete" msgstr "Inicio completado" msgctxt "current status of stack" msgid "Init Failed" msgstr "Inicio fallido" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Inicio en curso" msgid "Launch" msgstr "Iniciar " msgid "Launch Stack" msgstr "Lanzar pila" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "El nombre debe comenzar con una letra y sólo puede incluir letras, números, " "guiones bajos, puntos y guiones." msgid "Name of the stack to create." msgstr "Nombre de la pila a crear." msgid "Next" msgstr "Siguiente" msgid "Orchestration" msgstr "Orquestación" msgid "Overview" msgstr "Visión general" #, python-format msgid "Password for user \"%s\"" msgstr "Contraseña del usuario \"%s\"" #, python-format msgid "Please specify a %s using only one source method." msgstr "Especifique una %s utilizando solo un método de origen" msgid "Preview" msgstr "Vista previa" msgid "Preview Stack" msgstr "Previsualizar pila" msgid "Preview Stack Details" msgstr "Previsualizar detalles de la pila" msgid "Preview Stack Parameters" msgstr "Vista preliminar de parámetros de la pila" msgid "Preview Template" msgstr "Previsualizar plantilla" msgid "Resource" msgstr "Recurso" msgid "Resource Types" msgstr "Tipos de recurso" msgid "Resources" msgstr "Recursos" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Reanudar completado" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Reanudar fallido" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Reanudar en curso" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Reanudar pila" msgstr[1] "Reanudar pilas" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Pila reanudada" msgstr[1] "Pilas reanudadas" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Retroceso completado" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Retroceso fallido" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Retroceso en curso" msgid "Rollback On Failure" msgstr "Restauración tras fallo" msgid "Select Template" msgstr "Seleccionar plantilla" msgid "Select a new template to preview a stack." msgstr "Seleccione una nueva plantilla para una vista preliminar de una pila." msgid "Select a new template to re-launch a stack." msgstr "Seleccione una nueva plantilla para relanzar una pila." msgid "Select a template to launch a stack." msgstr "Seleccione una plantilla para iniciar una pila." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Instantánea completada" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Instantánea fallida" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Instantánea en curso" msgid "Stack Events" msgstr "Sucesos de pilas" msgid "Stack ID" msgstr "ID de pila" msgid "Stack Name" msgstr "Nombre de la pila" msgid "Stack Resource" msgstr "Recurso de pila" msgid "Stack Resource Type" msgstr "Tipo de recurso de pila" msgid "Stack Resources" msgstr "Recursos de pila" msgid "Stack creation started." msgstr "Se ha iniciado la creación de la pila." msgid "Stack creation timeout in minutes." msgstr "Tiempo de espera para la creación de la pila en minutos." msgid "Stack update started." msgstr "Se ha comenzado la actualización de la pila" msgid "Stacks" msgstr "Pilas" msgid "Status" msgstr "Estado" msgid "Status =" msgstr "Estado =" msgid "Status Reason" msgstr "Razón del estado" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspender completado" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspender fallido" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspender en curso" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Suspender pila" msgstr[1] "Suspender pilas" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Pila suspendida" msgstr[1] "Pilas suspendidas" msgid "Template" msgstr "Plantilla" msgid "Template Data" msgstr "Plantilla de datos" msgid "Template File" msgstr "Archivo de plantilla" msgid "Template Functions" msgstr "Plantilla de funciones" msgid "Template Source" msgstr "Origen de la plantilla" msgid "Template URL" msgstr "URL de plantilla" msgid "The raw contents of the environment file." msgstr "Los contenidos planos de el archivo de entorno." msgid "The raw contents of the template." msgstr "El contenido en bruto de la plantilla" #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "Ha habido un problema al analizar el %(prefix)s:%(error)s " msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Esto es indispensable para operaciones que se ejecuten durante el ciclo de " "vida de la pila." msgid "Time Since Event" msgstr "Tiempo desde el suceso" msgid "Topology" msgstr "Topología" msgid "Type" msgstr "Tipo" msgid "Type =" msgstr "Tipo =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "No se han podido obtener sucesos para la pila \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "No se han podido obtener recursos para la pila \"%s\"." msgid "Unable to retrieve metadata." msgstr "No se han podido recuperar los metadatos." msgid "Unable to retrieve resource type details." msgstr "No se han podido recuperar los detalles del tipo de recurso." msgid "Unable to retrieve resource." msgstr "No se ha podido recuperar el recurso." msgid "Unable to retrieve stack resource types." msgstr "No se han podido recuperar tipos de recurso de pila" msgid "Unable to retrieve stack template." msgstr "No se ha podido recuperar la plantilla de la pila" msgid "Unable to retrieve stack." msgstr "No se han podido recuperar las pilas." msgid "Update" msgstr "Actualizar" msgctxt "current status of stack" msgid "Update Complete" msgstr "Actualizar completado" msgctxt "current status of stack" msgid "Update Failed" msgstr "Actualizar fallido" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Actualizar en curso" msgid "Update Stack" msgstr "Actualizar plantilla" msgid "Update Stack Parameters" msgstr "Actualizar los parámetros de la pila" msgid "Updated" msgstr "Actualizado" msgid "Version" msgstr "Versión" msgid "You must specify a template via one of the available sources." msgstr "" "Debe especificar una plantilla mediante uno de los orígenes disponibles." msgid "environment" msgstr "entorno" msgid "template" msgstr "plantilla" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2622628 heat_dashboard-16.0.0/heat_dashboard/locale/fr/0000775000175000017500000000000015257164606020162 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3182616 heat_dashboard-16.0.0/heat_dashboard/locale/fr/LC_MESSAGES/0000775000175000017500000000000015257164606021747 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4994962 heat_dashboard-16.0.0/heat_dashboard/locale/fr/LC_MESSAGES/django.po0000664000175000017500000003417015257164522023553 0ustar00zuulzuul# SOURDET Henri , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Gérald LONLAS , 2016. #zanata # JF Taltavull , 2016. #zanata # Sylvie Chesneau , 2016. #zanata # Cédric Savignan , 2017. #zanata # JF Taltavull , 2017. #zanata # Loic Nicolle , 2017. #zanata # Pascal Larivée , 2017. #zanata # François Magimel , 2018. #zanata # Corinne Verheyde , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-05-02 09:40+0000\n" "Last-Translator: Corinne Verheyde \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s : %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s : %(stack_status_reason)s" msgid "A local environment to upload." msgstr "Environnement local à charger." msgid "A local template to upload." msgstr "Modèle local à charger." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "Un modèle permet d'automatiser le déploiement d'infrastructure, de services " "et d'applications." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adoption terminée" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Échec de l'adoption" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adoption en cours" msgid "An external (HTTP) URL to load the template from." msgstr "URL (HTTP) externe à partir de laquelle charger le modèle." msgid "Attributes" msgstr "Attributs" msgid "Case sensitive" msgstr "Sensible à la casse" msgid "Case-sensitive" msgstr "Sensible à la casse" msgid "Change Stack Template" msgstr "Changer le modèle de pile." msgid "Change Template" msgstr "Changer de modèle" msgctxt "current status of stack" msgid "Check Complete" msgstr "Vérification terminée" msgctxt "current status of stack" msgid "Check Failed" msgstr "Échec de la vérification" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Vérification en cours" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Vérifier la pile" msgstr[1] "Vérifier les piles" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Pile vérifiée" msgstr[1] "Piles vérifiées" msgid "Close" msgstr "Fermer" msgctxt "current status of stack" msgid "Create Complete" msgstr "Création terminée" msgctxt "current status of stack" msgid "Create Failed" msgstr "Échec de la création " msgctxt "current status of stack" msgid "Create In Progress" msgstr "Création en cours" msgid "Create Stack" msgstr "Créer une pile" msgid "Create a new stack with the provided values." msgstr "Créer une nouvelle pile avec les valeurs fournies." msgid "Created" msgstr "Créé" msgid "Creation Timeout (minutes)" msgstr "Délai d'attente de création (minutes)" msgid "Date Updated" msgstr "Date de mise à jour" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Suppression terminée" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Échec de la suppression" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Suppression en cours" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Supprimer la pile" msgstr[1] "Supprimer les piles" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Pile supprimée" msgstr[1] "Piles supprimées" msgid "Description" msgstr "Description" msgid "Description:" msgstr "Description :" msgid "Direct Input" msgstr "Entrée directe" #, fuzzy msgid "Disabled" msgstr "Désactivée" msgid "Edit Template" msgstr "Éditer le modèle" msgid "Enable rollback on create/update failure." msgstr "Permettre l'annulation en cas d'échec de création/mise à jour." #, fuzzy msgid "Enabled" msgstr "Activée" msgid "Environment Data" msgstr "Données d'environnement" msgid "Environment File" msgstr "Fichier d'environment" msgid "Environment Source" msgstr "Environment source" msgid "Events" msgstr "Événements" msgid "Export Draft" msgstr "Exporter un brouillon" msgid "File" msgstr "Fichier" msgid "Function" msgstr "Fonction" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "Importer un brouillon" msgctxt "current status of stack" msgid "Init Complete" msgstr "Initialisation terminée" msgctxt "current status of stack" msgid "Init Failed" msgstr "Échec de l'Initialisation " msgctxt "current status of stack" msgid "Init In Progress" msgstr "Initialisation en cours" msgid "Last Updated" msgstr "Dernière mise à jour" msgid "Launch" msgstr "Démarrer" msgid "Launch Parameters" msgstr "Paramètres de lancement" msgid "Launch Stack" msgstr "Lancer la pile" msgid "Links" msgstr "Liens" msgid "Load Draft" msgstr "Charger un brouillon" msgid "Manage Drafts" msgstr "Gérer les brouillons" msgid "Minutes" msgstr "Minutes" msgid "Name" msgstr "Nom" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Le nom doit commencer par une lettre et peut contenir uniquement des " "lettres, chiffres, traits de soulignement et traits d'union." msgid "Name of the stack to create." msgstr "Nom de la pile à créer." msgid "Next" msgstr "Suivant" msgid "Orchestration" msgstr "Orchestration" msgid "Outputs" msgstr "Sorties" msgid "Overview" msgstr "Vue d'ensemble" msgid "Parameters" msgstr "Paramètres" #, python-format msgid "Password for user \"%s\"" msgstr "Mot de passe pour l'utilisateur \"%s\"" #, python-format msgid "Please specify a %s using only one source method." msgstr "Indiquez un(e) %s utilisant une seule méthode source." msgid "Preview" msgstr "Prévisualiser" msgid "Preview Stack" msgstr "Prévisualiser la pile" msgid "Preview Stack Details" msgstr "Prévisualiser les détails de la pile" msgid "Preview Stack Parameters" msgstr "Prévisualiser les paramètres de pile" msgid "Preview Template" msgstr "Prévisualiser le modèle" msgid "Preview a new stack with the provided values." msgstr "Prévisualisez une nouvelle Stack avec ces nouvelles valeurs" msgid "Properties" msgstr "Propriétés" #, fuzzy msgid "Referenced Files" msgstr "Fichiers de référence" msgid "Resource" msgstr "Ressource" msgid "Resource ID" msgstr "ID de la ressource" msgid "Resource Metadata" msgstr "Métadonnées de la ressource" msgid "Resource Types" msgstr "Types de ressource" msgid "Resources" msgstr "Ressources" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Reprise terminée" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Échec de la reprise" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Reprise en cours" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Reprendre la pile" msgstr[1] "Reprendre les piles" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Pile reprise" msgstr[1] "Piles reprises" msgid "Rollback" msgstr "Retour arrière" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Annulation terminée" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Échec de l'annulation " msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Annulation en cours" msgid "Rollback On Failure" msgstr "Annulation en cas d'échec" msgid "Save Draft" msgstr "Sauvegarder un brouillon" msgid "Select Template" msgstr "Sélectionner un modèle" msgid "Select a new template to preview a stack." msgstr "Sélectionnez un nouveau modèle pour prévisualiser la pile." msgid "Select a new template to re-launch a stack." msgstr "Sélectionner un nouveau modèle pour relancer une pile." msgid "Select a template to launch a stack." msgstr "Sélectionner un modèle pour lancer une pile." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Instantané réussi" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Échec de l'instantané" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Instantané en cours" msgid "Stack Events" msgstr "Evénements de la pile" msgid "Stack ID" msgstr "ID de la pile" msgid "Stack ID =" msgstr "ID Pile = " msgid "Stack Name" msgstr "Nom de la pile" msgid "Stack Name =" msgstr "Nom de la stack =" msgid "Stack Parameters" msgstr "Paramètres de la pile" msgid "Stack Preview" msgstr "Prévisualisation de la pile" msgid "Stack Resource" msgstr "Ressource de la pile" msgid "Stack Resource ID" msgstr "ID de la ressource de Stack" msgid "Stack Resource Type" msgstr "Type de ressource de pile" msgid "Stack Resources" msgstr "Ressources de la pile" msgid "Stack creation started." msgstr "Création de pile commencée." msgid "Stack creation timeout in minutes." msgstr "Délai d'expiration (en minutes) de création de pile." msgid "Stack update started." msgstr "Mise à jour de pile commencée." msgid "Stack updating timeout in minutes." msgstr "Mise à jour du délai d'expiration dans quelques minutes par la Stack." msgid "Stacks" msgstr "Piles" msgid "Status" msgstr "Statut" msgid "Status =" msgstr "Statut =" msgid "Status Reason" msgstr "Raison du statut" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Interruption terminée" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Échec de l'interruption" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Interruption en cours" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Interrompre la pile" msgstr[1] "Interrompre les piles" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Pile interrompue" msgstr[1] "Piles interrompues" msgid "Template" msgstr "Modèle" msgid "Template Data" msgstr "Données du modèle" msgid "Template File" msgstr "Fichier du modèle" msgid "Template Functions" msgstr "Fonctions du modèle" msgid "Template Generator" msgstr "Générateur de modèle" msgid "Template Source" msgstr "Source du modèle" msgid "Template URL" msgstr "URL du modèle" msgid "Template Version" msgstr "Version du modèle" msgid "Template Versions" msgstr "Versions du modèle" msgid "The raw contents of the environment file." msgstr "Données brutes du fichier d'environnement." msgid "The raw contents of the template." msgstr "Données brutes du modèle." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "" "Un problème s'est produit lors de l'analyse syntaxique de %(prefix)s : " "%(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Obligatoire pour les opérations à effectuer tout au long du cycle de vie de " "la pile" msgid "Time Since Event" msgstr "Temps écoulé depuis l’évènement" msgid "Timeout" msgstr "Délai d'attente" msgid "Topology" msgstr "Topologie" msgid "Type" msgstr "Type" msgid "Type =" msgstr "Type =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Impossible d'obtenir les événements pour la pile \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Impossible d'obtenir les fonctions pour la version de modèle \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Impossible d'obtenir les ressources pour la pile \"%s\"." msgid "Unable to retrieve metadata." msgstr "Impossible de récupérer les métadonnées." msgid "Unable to retrieve resource type details." msgstr "Impossible de récupérer les détails du type de ressource." msgid "Unable to retrieve resource." msgstr "Impossible de récupérer la ressource." msgid "Unable to retrieve stack resource types." msgstr "Impossible de récupérer les types de ressource de la pile." msgid "Unable to retrieve stack template." msgstr "Impossible de récupérer le modèle de pile." msgid "Unable to retrieve stack." msgstr "Impossible d'extraire la pile." msgid "Unable to retrieve template functions." msgstr "Impossible de récupérer les fonctions du modèle." msgid "Unable to retrieve template versions." msgstr "Impossible de récupérer les versions du modèle." msgid "Update" msgstr "Mettre à jour" msgctxt "current status of stack" msgid "Update Complete" msgstr "Mise à jour terminée" msgctxt "current status of stack" msgid "Update Failed" msgstr "Échec de la mise à jour" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Mise à jour en cours" msgid "Update Stack" msgstr "Mise à jour de la pile" msgid "Update Stack Parameters" msgstr "Mettre à jour les paramètres de la Stack." msgid "Updated" msgstr "Mis à jour" msgid "Updating Timeout (minutes)" msgstr "Mise à jour du délai d'attente (en minutes) en cours" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "Utiliser une des options de modèle source disponibles pour préciser le " "modèle à utiliser lors du changement de cette Stack." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "Utiliser une des options de source de modèle disponible pour définir le " "modèle à utiliser lors de la création de cette pile. " msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "Utiliser une des options de modèle source disponibles pour préciser le " "modèle à utiliser lors de la prévisualition de cette Stack." msgid "Version" msgstr "Version" msgid "You must specify a template via one of the available sources." msgstr "Vous devez spécifier un modèle via l'une des sources disponibles." msgid "environment" msgstr "environnement" msgid "template" msgstr "modèle" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.4994962 heat_dashboard-16.0.0/heat_dashboard/locale/fr/LC_MESSAGES/djangojs.po0000664000175000017500000000665115257164522024113 0ustar00zuulzuul# Andreas Jaeger , 2016. #zanata # Gérald LONLAS , 2016. #zanata # Sylvie Chesneau , 2016. #zanata # Cédric Savignan , 2017. #zanata # Loic Nicolle , 2017. #zanata # François Magimel , 2018. #zanata # Corinne Verheyde , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-04-22 09:07+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-05-02 09:43+0000\n" "Last-Translator: Corinne Verheyde \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "Cancel" msgstr "Annuler" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Vérifier la Stack" msgstr[1] "Vérifier les Stacks" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Stack vérifiée : %s." msgstr[1] "Stacks vérifiées : %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Confirmer la vérification de la Stack" msgstr[1] "Confirmer la vérification des Stacks" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Confirmer la suppression de la pile" msgstr[1] "Confirmer la suppression des piles" msgid "Create Stack" msgstr "Créer une pile" msgid "Delete Resource" msgstr "Supprimer la ressource" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Supprimer la Stack" msgstr[1] "Supprimer les Stacks" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Stack supprimée : %s." msgstr[1] "Stacks supprimées : %s." msgid "Download" msgstr "Télécharger" msgid "Heat Orchestration Template" msgstr "Modèle d'orchestration Heat" msgid "OK" msgstr "OK" msgid "Save" msgstr "Sauvegarder" msgid "Saved Drafts" msgstr "Sauvegarder Brouillons" msgid "Show More Properties" msgstr "Plus de propriétés" #, python-format msgid "Stack %s was successfully created." msgstr "La Stack %s a été créée avec succès." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Impossible de vérifier la Stack : %s." msgstr[1] "Impossible de vérifier les Stacks : %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Impossible de supprimer la Stack : %s." msgstr[1] "Impossible de supprimer les Stacks : %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "Vous n'êtes pas autorisé à vérifier les Stacks : %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "Vous n'êtes pas autorisé à supprimer les Stacks : %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "Vous avez sélectionné \"%s\"." msgstr[1] "Vous avez sélectionné \"%s\"." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "" "Vous avez sélectionné \"%s\". La pile supprimée n'est pas récupérable." msgstr[1] "" "Vous avez sélectionné \"%s\". Les piles supprimées ne sont pas récupérables." ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2622628 heat_dashboard-16.0.0/heat_dashboard/locale/id/0000775000175000017500000000000015257164606020147 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3192616 heat_dashboard-16.0.0/heat_dashboard/locale/id/LC_MESSAGES/0000775000175000017500000000000015257164606021734 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5004961 heat_dashboard-16.0.0/heat_dashboard/locale/id/LC_MESSAGES/django.po0000664000175000017500000003264215257164523023543 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # suhartono , 2016. #zanata # suhartono , 2017. #zanata # suhartono , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-06-12 03:23+0000\n" "Last-Translator: suhartono \n" "Language-Team: Indonesian\n" "Language: id\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "Sebuah lingkungan lokal untuk meng-upload." msgid "A local template to upload." msgstr "Sebuah template lokal untuk meng-upload." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "Template digunakan untuk mengotomatisasi penyebaran infrastruktur, layanan, " "dan aplikasi." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adopt Complete" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adopt Failed" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adopt In Progress" msgid "An external (HTTP) URL to load the template from." msgstr "URL (HTTP) Eksternal untuk memuat template" msgid "Attributes" msgstr "Attributes" msgid "Case sensitive" msgstr "Case sensitive (case sensitive)" msgid "Case-sensitive" msgstr "Case-sensitive (case sensitive)" msgid "Change Stack Template" msgstr "Change Stack Template" msgid "Change Template" msgstr "Merubah template" msgctxt "current status of stack" msgid "Check Complete" msgstr "Check Complete" msgctxt "current status of stack" msgid "Check Failed" msgstr "Check Failed" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Check In Progress" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Check Stack" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Checked Stack" msgid "Clear Canvas" msgstr "Clear Canvas" msgid "Close" msgstr "Close" msgctxt "current status of stack" msgid "Create Complete" msgstr "Create Complete" msgctxt "current status of stack" msgid "Create Failed" msgstr "Create Failed" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Create In Progress" msgid "Create Stack" msgstr "Buat stack " msgid "Create a new stack with the provided values." msgstr "Buat tumpukan (stack) baru dengan nilai yang diberikan." msgid "Created" msgstr "Created" msgid "Creation Timeout (minutes)" msgstr "Creation Timeout (minutes)" msgid "Date Updated" msgstr "Date Updated" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Delete Complete" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Delete Failed" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Delete In Progress" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Delete Stack" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Deleted Stack" msgid "Description" msgstr "Deskripsi" msgid "Description:" msgstr "Description:" msgid "Direct Input" msgstr "Direct Input (masukan langsung)" msgid "Disabled" msgstr "Disabled" msgid "Edit Template" msgstr "Edit Template (edit template)" msgid "Enable rollback on create/update failure." msgstr "Aktifkan rollback pada kegagalan create/update." msgid "Enabled" msgstr "Enabled" msgid "Environment Data" msgstr "Environment Data" msgid "Environment File" msgstr "Environment File" msgid "Environment Source" msgstr "Environment Source" msgid "Events" msgstr "Acara" msgid "Export Draft" msgstr "Export Draft" msgid "File" msgstr "File" msgid "Function" msgstr "Function" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "Import Draft" msgctxt "current status of stack" msgid "Init Complete" msgstr "Init Complete" msgctxt "current status of stack" msgid "Init Failed" msgstr "Init Failed" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Init In Progress" msgid "Last Updated" msgstr "Last Updated" msgid "Launch" msgstr "Launch (luncurkan)" msgid "Launch Parameters" msgstr "Launch Parameters" msgid "Launch Stack" msgstr "Launch Stack" msgid "Links" msgstr "Links" msgid "Load Draft" msgstr "Load Draft" msgid "Manage Drafts" msgstr "Manage Drafts" msgid "Minutes" msgstr "Minutes" msgid "Name" msgstr "Name" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Nama harus dimulai dengan huruf dan hanya dapat berisi huruf, angka, garis " "bawah, periode dan tanda hubung." msgid "Name of the stack to create." msgstr "Nama dari tumpukan untuk membuat." msgid "Next" msgstr "Next (berikutnya)" msgid "Orchestration" msgstr "Orchestration" msgid "Outputs" msgstr "Outputs" msgid "Overview" msgstr "Ikhtisar" msgid "Parameters" msgstr "Parameters" #, python-format msgid "Password for user \"%s\"" msgstr "Password untuk pengguna \"%s\"" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "Silahkan seret dan lepas ikon sumber daya ke kanvas tengah untuk menentukan " "sumber daya untuk template." #, python-format msgid "Please specify a %s using only one source method." msgstr "Silakan tentukan %s hanya menggunakan satu metode sumber." msgid "Preview" msgstr "Preview" msgid "Preview Stack" msgstr "Preview Stack" msgid "Preview Stack Details" msgstr "Preview Stack Details" msgid "Preview Stack Parameters" msgstr "Preview Stack Parameters" msgid "Preview Template" msgstr "Preview Template (lihat template)" msgid "Preview a new stack with the provided values." msgstr "Pratinjau stack baru dengan nilai yang diberikan." msgid "Properties" msgstr "Properties" msgid "Referenced Files" msgstr "Referenced Files" msgid "Resource" msgstr "Resource" msgid "Resource ID" msgstr "Resource ID" msgid "Resource Metadata" msgstr "Resource Metadata" msgid "Resource Types" msgstr "Resource Types (tipe sumber daya)" msgid "Resources" msgstr "Sumber daya" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Resume Complete" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Resume Failed" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Resume In Progress" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Resume Stack" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Resumed Stack" msgid "Rollback" msgstr "Rollback" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Rollback Complete" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Rollback Failed" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Rollback In Progress" msgid "Rollback On Failure" msgstr "Rollback On Failure" msgid "Save Draft" msgstr "Save Draft" msgid "Select Template" msgstr "Pilih template" msgid "Select a new template to preview a stack." msgstr "Pilih template baru untuk melihat setumpuk (stack)." msgid "Select a new template to re-launch a stack." msgstr "Pilih template baru untuk meluncurkan kembali tumpukan (stack)." msgid "Select a template to launch a stack." msgstr "Pilih template untuk meluncurkan stack." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Snapshot Complete" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Snapshot Failed" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Snapshot In Progress" msgid "Stack Events" msgstr "Stack Events" msgid "Stack ID" msgstr "Stack ID (ID stack)" msgid "Stack ID =" msgstr "Stack ID = (stack ID=)" msgid "Stack Name" msgstr "Stack Name (nama stack)" msgid "Stack Name =" msgstr "Stack Name =" msgid "Stack Parameters" msgstr "Stack Parameters" msgid "Stack Preview" msgstr "Stack Preview" msgid "Stack Resource" msgstr "Stack Resource" msgid "Stack Resource ID" msgstr "Stack Resource ID" msgid "Stack Resource Type" msgstr "Stack Resource Type" msgid "Stack Resources" msgstr "Stack Resources" msgid "Stack creation started." msgstr "Pembuatan Stack mulai." msgid "Stack creation timeout in minutes." msgstr "Batas waktu pembuatan stack dalam hitungan menit." msgid "Stack update started." msgstr "Stack pembaruan mulai." msgid "Stack updating timeout in minutes." msgstr "Stack pembaruan waktu tunggu (timeout) dalam hitungan menit." msgid "Stacks" msgstr "Stacks" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Status = " msgid "Status Reason" msgstr "Status Reason" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspend Complete" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspend Failed" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspend In Progress" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Suspend Stack" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Suspended Stack" msgid "Template" msgstr "Template" msgid "Template Data" msgstr "Template Data" msgid "Template File" msgstr "Template File" msgid "Template Functions" msgstr "Template Functions" msgid "Template Generator" msgstr "Template Generator" msgid "Template Source" msgstr "Template Source" msgid "Template URL" msgstr "URL template" msgid "Template Version" msgstr "Template Version" msgid "Template Versions" msgstr "Template Versions" msgid "The raw contents of the environment file." msgstr "Isi baku file lingkungan." msgid "The raw contents of the template." msgstr "Isi baku template." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "Ada masalah parsing (analisa) %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Hal ini diperlukan untuk operasi yang akan dilakukan di seluruh siklus hidup " "stack" msgid "Time Since Event" msgstr "Time Since Event" msgid "Timeout" msgstr "Timeout" msgid "Topology" msgstr "Topology (topologi)" msgid "Type" msgstr "Type" msgid "Type =" msgstr "Type = (tipe=)" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Tidak dapat mendapatkan events untuk stack \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Tidak dapat mendapatkan fungsi untuk versi template \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Tidak dapat mendapatkan sumber daya untuk stack \"%s\"." msgid "Unable to retrieve metadata." msgstr "Tidak dapat mengambil metadata." msgid "Unable to retrieve resource type details." msgstr "Tidak dapat mengambil rincian jenis sumber daya." msgid "Unable to retrieve resource." msgstr "Tidak dapat mengambil sumber daya." msgid "Unable to retrieve stack resource types." msgstr "Tidak dapat mengambil jenis sumber daya stack." msgid "Unable to retrieve stack template." msgstr "Tidak dapat mengambil template stack." msgid "Unable to retrieve stack." msgstr "Tidak dapat mengambil tumpukan (stack)." msgid "Unable to retrieve template functions." msgstr "Tidak dapat mengambil fungsi template." msgid "Unable to retrieve template versions." msgstr "Tidak dapat mengambil versi Template." msgid "Update" msgstr "Update" msgctxt "current status of stack" msgid "Update Complete" msgstr "Update Complete" msgctxt "current status of stack" msgid "Update Failed" msgstr "Update Failed" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Update In Progress" msgid "Update Stack" msgstr "Update Stack" msgid "Update Stack Parameters" msgstr "Update Stack Parameters" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "Perbarui stack dengan nilai yang diberikan. Harap dicatat bahwa setiap " "parameter terenkripsi, seperti kata sandi, akan disetel ulang menjadi " "default jika Anda tidak mengubahnya di sini." msgid "Updated" msgstr "Updated" msgid "Updating Timeout (minutes)" msgstr "Memperbarui Timeout (menit)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "Gunakan salah satu opsi sumber template yang tersedia untuk menentukan " "template yang akan digunakan dalam mengubah stack ini." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "Gunakan salah satu pilihan sumber template yang tersedia untuk menentukan " "template yang akan digunakan dalam membuat tumpukan (stack) ini." msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "Gunakan salah satu pilihan sumber template yang tersedia untuk menentukan " "template yang akan digunakan dalam melihat pratinjau stack ini." msgid "Version" msgstr "Version" msgid "You must specify a template via one of the available sources." msgstr "" "Anda harus menentukan template melalui salah satu sumber yang tersedia." msgid "environment" msgstr "environment" msgid "template" msgstr "template" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5004961 heat_dashboard-16.0.0/heat_dashboard/locale/id/LC_MESSAGES/djangojs.po0000664000175000017500000000516215257164523024075 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # suhartono , 2016. #zanata # suhartono , 2017. #zanata # suhartono , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-06-14 03:57+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-06-13 07:54+0000\n" "Last-Translator: suhartono \n" "Language-Team: Indonesian\n" "Language: id\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" msgid "Cancel" msgstr "Cancel" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Check Stack" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Checked Stack: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Confirm Check Stack" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Confirm Delete Stack" msgid "Create Stack" msgstr "Buat stack " msgid "Delete Resource" msgstr "Delete Resource" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Delete Stack" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Deleted Stack: %s." msgid "Download" msgstr "Download (pengunduhan)" msgid "Heat Orchestration Template" msgstr "Heat Orchestration Template" msgid "OK" msgstr "OK" msgid "Save" msgstr "Simpan" msgid "Saved Drafts" msgstr "Saved Drafts" msgid "Show More Properties" msgstr "Show More Properties" #, python-format msgid "Stack %s was successfully created." msgstr "Stack %s berhasil dibuat." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Unable to check Stack: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Unable to delete Stack: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "Anda tidak diizinkan untuk memeriksa stack: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "Anda tidak diizinkan untuk menghapus stacks: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "You have selected \"%s\"." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "You have selected \"%s\". Deleted stack is not recoverable." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.263263 heat_dashboard-16.0.0/heat_dashboard/locale/it/0000775000175000017500000000000015257164606020167 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3192616 heat_dashboard-16.0.0/heat_dashboard/locale/it/LC_MESSAGES/0000775000175000017500000000000015257164606021754 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.501496 heat_dashboard-16.0.0/heat_dashboard/locale/it/LC_MESSAGES/django.po0000664000175000017500000002326415257164523023563 0ustar00zuulzuul# Alessandra , 2015. #zanata # OpenStack Infra , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Doraly Navarro Meinhardt , 2016. #zanata # Remo Mattei , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-05-08 09:39+0000\n" "Last-Translator: Doraly Navarro Meinhardt \n" "Language-Team: Italian\n" "Language: it\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "A local environment to upload." msgstr "Un ambiente locale da caricare." msgid "A local template to upload." msgstr "Un template locale da caricare." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adozione completata" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adozione non riuscita" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adozione in corso" msgid "An external (HTTP) URL to load the template from." msgstr "Un URL (HTTP) esterno da cui caricare il template." msgid "Change Stack Template" msgstr "Modifica template stack" msgid "Change Template" msgstr "Modifica template" msgctxt "current status of stack" msgid "Check Complete" msgstr "Verifica completata" msgctxt "current status of stack" msgid "Check Failed" msgstr "Verifica non riuscita" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Verifica in corso" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Verifica stack" msgstr[1] "Verifica stack" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Stack verificato" msgstr[1] "Stack verificati" msgctxt "current status of stack" msgid "Create Complete" msgstr "Creazione completata" msgctxt "current status of stack" msgid "Create Failed" msgstr "Creazione non riuscita" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Creazione in corso" msgid "Create Stack" msgstr "Crea stack" msgid "Created" msgstr "Creato" msgid "Creation Timeout (minutes)" msgstr "Timeout creazione (minuti)" msgid "Date Updated" msgstr "Data aggiornata" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Eliminazione completata" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Eliminazione non riuscita" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Eliminazione in corso" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Elimina stack" msgstr[1] "Elimina stack" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Stack eliminato" msgstr[1] "Stack eliminati" msgid "Description" msgstr "Descrizione" msgid "Direct Input" msgstr "Input diretto" msgid "Edit Template" msgstr "Modifica template" msgid "Enable rollback on create/update failure." msgstr "Abilita rollback per gli errori di creazione o aggiornamento." msgid "Environment Data" msgstr "Dati ambientali" msgid "Environment File" msgstr "File di ambiente" msgid "Environment Source" msgstr "Origine ambiente" msgid "Events" msgstr "Eventi" msgid "File" msgstr "File" msgctxt "current status of stack" msgid "Init Complete" msgstr "Inizializzazione completata" msgctxt "current status of stack" msgid "Init Failed" msgstr "Inizializzazione non riuscita" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Inizializzazione in corso" msgid "Launch" msgstr "Avvia" msgid "Launch Stack" msgstr "Avvia stack" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Il nome deve iniziare con una lettera e può contenere solo lettere, numeri, " "sottolineature, punti e trattini." msgid "Name of the stack to create." msgstr "Nome dello stack da creare." msgid "Next" msgstr "Successivo" msgid "Orchestration" msgstr "Orchestrazione" msgid "Overview" msgstr "Panoramica" #, python-format msgid "Password for user \"%s\"" msgstr "Password per utente \"%s\"" #, python-format msgid "Please specify a %s using only one source method." msgstr "Specificare un %s utilizzando un solo metodo di origine." msgid "Preview" msgstr "Anteprima" msgid "Preview Stack" msgstr "Anteprima stack" msgid "Preview Stack Details" msgstr "Dettagli anteprima stack" msgid "Preview Stack Parameters" msgstr "Parametri anteprima stack" msgid "Preview Template" msgstr "Template di anteprima" msgid "Resource" msgstr "Risorsa" msgid "Resource Types" msgstr "Tipi di risorsa" msgid "Resources" msgstr "Risorse" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Ripristino completato" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Ripristino non riuscito" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Ripristino in corso" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Riprendi stack" msgstr[1] "Riprendi stack" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Stack ripreso" msgstr[1] "Stack ripresi" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Rollback completato" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Rollback non riuscito" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Rollback in corso" msgid "Rollback On Failure" msgstr "Rollback in caso di errore" msgid "Select Template" msgstr "Seleziona template" msgid "Select a new template to preview a stack." msgstr "" "Selezionare un nuovo template per visualizzare un'anteprima di uno stack." msgid "Select a new template to re-launch a stack." msgstr "Selezionare un nuovo template per riavviare uno stack." msgid "Select a template to launch a stack." msgstr "Selezionare un template per avviare uno stack." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Istantanea completata" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Istantanea non riuscita" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Istantanea in corso" msgid "Stack Events" msgstr "Eventi stack" msgid "Stack ID" msgstr "ID stack" msgid "Stack Name" msgstr "Nome stack" msgid "Stack Resource" msgstr "Risorsa stack" msgid "Stack Resource Type" msgstr "Tipo risorsa dello stack" msgid "Stack Resources" msgstr "Risorse stack" msgid "Stack creation started." msgstr "Creazione stack avviata." msgid "Stack creation timeout in minutes." msgstr "Timeout di creazione stack in minuti." msgid "Stack update started." msgstr "Aggiornamento stack avviato." msgid "Stacks" msgstr "Stack" msgid "Status" msgstr "Stato" msgid "Status =" msgstr "Stato =" msgid "Status Reason" msgstr "Motivo dello stato" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Sospensione completata" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Sospensione non riuscita" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Sospensione in corso" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Sospendi stack" msgstr[1] "Sospendi stack" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Stack sospeso" msgstr[1] "Stack sospesi" msgid "Template" msgstr "Template" msgid "Template Data" msgstr "Dati template" msgid "Template File" msgstr "File template" msgid "Template Source" msgstr "Origine template" msgid "Template URL" msgstr "URL template" msgid "The raw contents of the environment file." msgstr "Il contenuto non elaborato del file di ambiente." msgid "The raw contents of the template." msgstr "Il contenuto non elaborato del template." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "Si è verificato un problema durante l'analisi di %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Obbligatoria per le operazioni da eseguire durante il ciclo di vita dello " "stack" msgid "Time Since Event" msgstr "Tempo trascorso dall'evento" msgid "Topology" msgstr "Topologia" msgid "Type" msgstr "Tipo" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Impossibile ottenere gli eventi per lo stack \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Impossibile ottenere le risorse per lo stack \"%s\"." msgid "Unable to retrieve metadata." msgstr "Impossibile richiamare i metadati." msgid "Unable to retrieve resource type details." msgstr "Impossibile richiamare i dettagli del tipo di risorsa." msgid "Unable to retrieve resource." msgstr "Impossibile richiamare la risorsa." msgid "Unable to retrieve stack resource types." msgstr "Impossibile richiamare i tipi di risorsa stack." msgid "Unable to retrieve stack template." msgstr "Impossibile richiamare il template stack." msgid "Unable to retrieve stack." msgstr "Impossibile richiamare lo stack." msgid "Update" msgstr "Aggiorna" msgctxt "current status of stack" msgid "Update Complete" msgstr "Aggiornamento completato" msgctxt "current status of stack" msgid "Update Failed" msgstr "Aggiornamento non riuscito" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Aggiornamento in corso" msgid "Update Stack" msgstr "Aggiorna stack" msgid "Update Stack Parameters" msgstr "Aggiorna parametri stack" msgid "Updated" msgstr "Aggiornato" msgid "Version" msgstr "Versione" msgid "You must specify a template via one of the available sources." msgstr "" "È necessario specificare un template mediante una delle origini disponibili." msgid "environment" msgstr "ambiente" msgid "template" msgstr "template" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.263263 heat_dashboard-16.0.0/heat_dashboard/locale/ja/0000775000175000017500000000000015257164606020145 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3202617 heat_dashboard-16.0.0/heat_dashboard/locale/ja/LC_MESSAGES/0000775000175000017500000000000015257164606021732 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5024962 heat_dashboard-16.0.0/heat_dashboard/locale/ja/LC_MESSAGES/django.po0000664000175000017500000003566515257164523023551 0ustar00zuulzuul# Yuko Katabami , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-09-13 08:03+0000\n" "Last-Translator: Yuko Katabami \n" "Language-Team: Japanese\n" "Language: ja\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "アップロードã�™ã‚‹å¿…è¦�ã�®ã�‚るローカルã�®ç’°å¢ƒè¨­å®šãƒ‡ãƒ¼ã‚¿" msgid "A local template to upload." msgstr "アップロードã�™ã‚‹å¿…è¦�ã�®ã�‚るローカルã�®ãƒ†ãƒ³ãƒ—レート" msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "テンプレートã�¯ã€�インフラストラクãƒ�ャーã€�サービスã€�ã�Šã‚ˆã�³ã‚¢ãƒ—リケーションã�®ãƒ‡" "プロイメントã�®è‡ªå‹•化ã�«ä½¿ç”¨ã�•れã�¾ã�™ã€‚" msgctxt "current status of stack" msgid "Adopt Complete" msgstr "導入完了" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "å°Žå…¥ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "導入中" msgid "An external (HTTP) URL to load the template from." msgstr "テンプレートを読ã�¿è¾¼ã‚€å¤–部 (HTTP) URL" msgid "Attributes" msgstr "属性" msgid "Case sensitive" msgstr "大文字å°�文字を区別" msgid "Case-sensitive" msgstr "大文字å°�文字を区別" msgid "Change Stack Template" msgstr "スタックテンプレートã�®å¤‰æ›´" msgid "Change Template" msgstr "テンプレートã�®å¤‰æ›´" msgctxt "current status of stack" msgid "Check Complete" msgstr "ãƒ�ェック完了" msgctxt "current status of stack" msgid "Check Failed" msgstr "ãƒ�ェックã�«å¤±æ•—" msgctxt "current status of stack" msgid "Check In Progress" msgstr "ãƒ�ェック中" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "スタックã�®ãƒ�ェック" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "ãƒ�ェック済ã�¿ã�®ã‚¹ã‚¿ãƒƒã‚¯" msgid "Clear Canvas" msgstr "キャンãƒ�スã�®ã‚¯ãƒªã‚¢" msgid "Close" msgstr "é–‰ã�˜ã‚‹" msgctxt "current status of stack" msgid "Create Complete" msgstr "作æˆ�完了" msgctxt "current status of stack" msgid "Create Failed" msgstr "作æˆ�ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Create In Progress" msgstr "作æˆ�中" msgid "Create Stack" msgstr "スタックã�®ä½œæˆ�" msgid "Create a new stack with the provided values." msgstr "指定ã�•れã�Ÿå€¤ã‚’用ã�„ã�¦æ–°ã�—ã�„スタックを作æˆ�ã�—ã�¾ã�™ã€‚" msgid "Created" msgstr "作æˆ�時刻" msgid "Creation Timeout (minutes)" msgstr "作æˆ�タイムアウト時間 (分å�˜ä½�)" msgid "Date Updated" msgstr "æ›´æ–°æ—¥" msgctxt "current status of stack" msgid "Delete Complete" msgstr "削除完了" msgctxt "current status of stack" msgid "Delete Failed" msgstr "削除ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "削除中" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "スタックã�®å‰Šé™¤" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "スタックを削除ã�—ã�¾ã�—ã�Ÿ" msgid "Description" msgstr "説明" msgid "Description:" msgstr "説明:" msgid "Direct Input" msgstr "直接入力" msgid "Disabled" msgstr "無効" msgid "Edit Template" msgstr "テンプレートã�®ç·¨é›†" msgid "Enable rollback on create/update failure." msgstr "作æˆ�/更新失敗時ã�®ãƒ­ãƒ¼ãƒ«ãƒ�ックを有効ã�«ã�—ã�¾ã�™ã€‚" msgid "Enabled" msgstr "有効" msgid "Environment Data" msgstr "環境設定データ" msgid "Environment File" msgstr "環境設定ファイル" msgid "Environment Source" msgstr "環境設定ã�®æŒ‡å®šæ–¹æ³•" msgid "Events" msgstr "イベント" msgid "Export Draft" msgstr "ドラフトã�®ã‚¨ã‚¯ã‚¹ãƒ�ート" msgid "File" msgstr "ファイル" msgid "Function" msgstr "関数" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "ドラフトã�®ã‚¤ãƒ³ãƒ�ート" msgctxt "current status of stack" msgid "Init Complete" msgstr "init 完了" msgctxt "current status of stack" msgid "Init Failed" msgstr "init ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Init In Progress" msgstr "init 実行中" msgid "Last Updated" msgstr "最終更新" msgid "Launch" msgstr "èµ·å‹•" msgid "Launch Parameters" msgstr "起動パラメーター" msgid "Launch Stack" msgstr "スタックã�®èµ·å‹•" msgid "Links" msgstr "リンク" msgid "Load Draft" msgstr "ドラフトã�®èª­ã�¿è¾¼ã�¿" msgid "Manage Drafts" msgstr "ドラフトã�®ç®¡ç�†" msgid "Minutes" msgstr "分" msgid "Name" msgstr "å��å‰�" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "å��å‰�ã�® 1 文字目ã�«ã�¯ã‚¢ãƒ«ãƒ•ァベットを指定ã�™ã‚‹å¿…è¦�ã�Œã�‚りã�¾ã�™ã€‚ã�¾ã�Ÿã€�å��å‰�ã�«ä½¿ç”¨ã�§" "ã��ã‚‹ã�®ã�¯ã€�英数字ã€�アンダースコアã€�ピリオドã€�ãƒ�イフンã�®ã�¿ã�§ã�™ã€‚" msgid "Name of the stack to create." msgstr "作æˆ�ã�™ã‚‹ã‚¹ã‚¿ãƒƒã‚¯ã�®å��å‰�。" msgid "Next" msgstr "次ã�¸" msgid "Orchestration" msgstr "オーケストレーション" msgid "Outputs" msgstr "出力" msgid "Overview" msgstr "概è¦�" msgid "Parameters" msgstr "パラメーター" #, python-format msgid "Password for user \"%s\"" msgstr "ユーザー \"%s\" ã�®ãƒ‘スワード" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "リソースã�®ã‚¢ã‚¤ã‚³ãƒ³ã‚’ç”»é�¢ä¸­å¤®ã�®ãƒ†ãƒ³ãƒ—レートã�«ãƒ‰ãƒ©ãƒƒã‚°ã‚¢ãƒ³ãƒ‰ãƒ‰ãƒ­ãƒƒãƒ—ã�—ã�¦ã€�テン" "プレートã�§ãƒªã‚½ãƒ¼ã‚¹ã‚’定義ã�—ã�¾ã�™ã€‚" #, python-format msgid "Please specify a %s using only one source method." msgstr "ソースメソッドã�Œ 1 ã�¤ã� ã�‘ã�® %s を指定ã�—ã�¦ã��ã� ã�•ã�„。" msgid "Preview" msgstr "プレビュー" msgid "Preview Stack" msgstr "スタックã�®ãƒ—レビュー" msgid "Preview Stack Details" msgstr "プレビューã�—ã�Ÿã‚¹ã‚¿ãƒƒã‚¯ã�®è©³ç´°" msgid "Preview Stack Parameters" msgstr "プレビューã�™ã‚‹ã‚¹ã‚¿ãƒƒã‚¯ã�®ãƒ‘ラメーター" msgid "Preview Template" msgstr "テンプレートã�®ãƒ—レビュー" msgid "Preview a new stack with the provided values." msgstr "指定ã�•れã�Ÿå€¤ã‚’用ã�„ã�¦æ–°ã�—ã�„スタックをプレビューã�—ã�¾ã�™ã€‚" msgid "Properties" msgstr "プロパティー" msgid "Referenced Files" msgstr "å�‚ç…§ã�•れã�Ÿãƒ•ァイル" msgid "Resource" msgstr "リソース" msgid "Resource ID" msgstr "リソース ID" msgid "Resource Metadata" msgstr "リソースã�®ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿" msgid "Resource Types" msgstr "リソース種別" msgid "Resources" msgstr "リソース" msgctxt "current status of stack" msgid "Resume Complete" msgstr "å†�開完了" msgctxt "current status of stack" msgid "Resume Failed" msgstr "å†�é–‹ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "å†�開中" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "スタックã�®å†�é–‹" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "スタックをå†�é–‹ã�—ã�¾ã�—ã�Ÿã€‚" msgid "Rollback" msgstr "ロールãƒ�ック" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "ロールãƒ�ック完了" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "ロールãƒ�ックã�«å¤±æ•—" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "ロールãƒ�ック中" msgid "Rollback On Failure" msgstr "失敗時ã�®ãƒ­ãƒ¼ãƒ«ãƒ�ック" msgid "Save Draft" msgstr "ドラフトã�®ä¿�å­˜" msgid "Select Template" msgstr "テンプレートã�®é�¸æŠž" msgid "Select a new template to preview a stack." msgstr "プレビューã�™ã‚‹ã‚¹ã‚¿ãƒƒã‚¯ã�®ãƒ†ãƒ³ãƒ—レートをé�¸æŠžã�—ã�¦ã��ã� ã�•ã�„。" msgid "Select a new template to re-launch a stack." msgstr "スタックã�®å†�èµ·å‹•ã�«ä½¿ã�†ãƒ†ãƒ³ãƒ—レートをé�¸æŠžã�—ã�¦ã��ã� ã�•ã�„" msgid "Select a template to launch a stack." msgstr "スタックã�®èµ·å‹•ã�«ä½¿ã�†ãƒ†ãƒ³ãƒ—レートをé�¸æŠžã�—ã�¦ã��ã� ã�•ã�„" msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "スナップショット作æˆ�完了" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "スナップショットã�®ä½œæˆ�ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "スナップショット作æˆ�中" msgid "Stack Events" msgstr "スタックイベント" msgid "Stack ID" msgstr "スタック ID" msgid "Stack ID =" msgstr "スタック ID =" msgid "Stack Name" msgstr "スタックå��" msgid "Stack Name =" msgstr "スタックå�� =" msgid "Stack Parameters" msgstr "スタックã�®ãƒ‘ラメーター" msgid "Stack Preview" msgstr "スタックã�®ãƒ—レビュー" msgid "Stack Resource" msgstr "スタックリソース" msgid "Stack Resource ID" msgstr "スタックリソース ID" msgid "Stack Resource Type" msgstr "スタックリソース種別" msgid "Stack Resources" msgstr "スタックリソース" msgid "Stack creation started." msgstr "スタックã�®ä½œæˆ�ã‚’é–‹å§‹ã�—ã�¾ã�—ã�Ÿã€‚" msgid "Stack creation timeout in minutes." msgstr "スタック作æˆ�ã�®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆæ™‚é–“ (分å�˜ä½�)。" msgid "Stack update started." msgstr "スタックã�®æ›´æ–°ã‚’é–‹å§‹ã�—ã�¾ã�—ã�Ÿã€‚" msgid "Stack updating timeout in minutes." msgstr "スタック更新ã�®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ (分å�˜ä½�)" msgid "Stacks" msgstr "スタック" msgid "Status" msgstr "ステータス" msgid "Status =" msgstr "ステータス =" msgid "Status Reason" msgstr "ステータスã�®ç�†ç”±" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "休止完了" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "休止ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "休止中" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "スタックã�®ä¼‘æ­¢" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "スタックを休止ã�—ã�¾ã�—ã�Ÿ" msgid "Template" msgstr "テンプレート" msgid "Template Data" msgstr "テンプレートデータ" msgid "Template File" msgstr "テンプレートファイル" msgid "Template Functions" msgstr "テンプレート関数" msgid "Template Generator" msgstr "テンプレートジェãƒ�レーター" msgid "Template Source" msgstr "テンプレートã�®æŒ‡å®šæ–¹æ³•" msgid "Template URL" msgstr "テンプレート URL" msgid "Template Version" msgstr "テンプレートãƒ�ージョン" msgid "Template Versions" msgstr "テンプレートãƒ�ージョン" msgid "The raw contents of the environment file." msgstr "環境設定ファイルã�® RAW データ" msgid "The raw contents of the template." msgstr "テンプレートã�® RAW データ。" #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "%(prefix)s ã�®è§£æž�中ã�«å•�題ã�Œã�‚りã�¾ã�—ã�Ÿ: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "スタックã�®ãƒ©ã‚¤ãƒ•サイクル全体を通ã�—ã�¦ã€�å�„種æ“�作ã�®å®Ÿè¡Œã�«å¿…è¦�ã�§ã�™ã€‚" msgid "Time Since Event" msgstr "イベントã�‹ã‚‰ã�®çµŒé�Žæ™‚é–“" msgid "Timeout" msgstr "タイムアウト" msgid "Topology" msgstr "トãƒ�ロジー" msgid "Type" msgstr "種別" msgid "Type =" msgstr "種別 =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "スタック \"%s\" ã�®ã‚¤ãƒ™ãƒ³ãƒˆã‚’å�–å¾—ã�§ã��ã�¾ã�›ã‚“。" #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "テンプレートãƒ�ージョン「%sã€�ã�®é–¢æ•°ã‚’å�–å¾—ã�§ã��ã�¾ã�›ã‚“。" #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "スタック \"%s\" ã�®ãƒªã‚½ãƒ¼ã‚¹ã‚’å�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve metadata." msgstr "メタデータをå�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve resource type details." msgstr "リソース種別ã�®è©³ç´°ã‚’å�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve resource." msgstr "リソースをå�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve stack resource types." msgstr "スタックã�®ãƒªã‚½ãƒ¼ã‚¹ç¨®åˆ¥ã�®ä¸€è¦§ã‚’å�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve stack template." msgstr "スタックテンプレートをå�–å¾—ã�§ã��ã�¾ã�›ã‚“" msgid "Unable to retrieve stack." msgstr "スタックをå�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve template functions." msgstr "テンプレートã�®é–¢æ•°ä¸€è¦§ã‚’å�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Unable to retrieve template versions." msgstr "テンプレートãƒ�ージョン一覧をå�–å¾—ã�§ã��ã�¾ã�›ã‚“。" msgid "Update" msgstr "æ›´æ–°" msgctxt "current status of stack" msgid "Update Complete" msgstr "更新完了" msgctxt "current status of stack" msgid "Update Failed" msgstr "æ›´æ–°ã�«å¤±æ•—" msgctxt "current status of stack" msgid "Update In Progress" msgstr "更新中" msgid "Update Stack" msgstr "スタックã�®æ›´æ–°" msgid "Update Stack Parameters" msgstr "スタックã�®ãƒ‘ラメーターã�®æ›´æ–°" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "スタックを指定ã�—ã�Ÿå€¤ã�§æ›´æ–°ã�—ã�¾ã�™ã€‚パスワードã�ªã�©ã�®ã€�æš—å�·åŒ–ã�•れるパラメーター" "ã�¯ã€�ã�“ã�“ã�§æŒ‡å®šã�—ã�ªã�‹ã�£ã�Ÿå ´å�ˆã€�デフォルト値ã�«ãƒªã‚»ãƒƒãƒˆã�•れる点ã�«ã�¯æ³¨æ„�ã�—ã�¦ã��ã� " "ã�•ã�„。" msgid "Updated" msgstr "更新時刻" msgid "Updating Timeout (minutes)" msgstr "æ›´æ–°ã�®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ (分å�˜ä½�)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "利用å�¯èƒ½ã�ªãƒ†ãƒ³ãƒ—レートã�®æŒ‡å®šæ–¹æ³•ã�®ã‚ªãƒ—ションã�‹ã‚‰ã€�ã�“ã�®ã‚¹ã‚¿ãƒƒã‚¯ã�®å¤‰æ›´ã�«ä½¿ç”¨ã�™" "るテンプレートã�®æŒ‡å®šæ–¹æ³•ã‚’é�¸æŠžã�—ã�¦ã��ã� ã�•ã�„。" msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "利用å�¯èƒ½ã�ªãƒ†ãƒ³ãƒ—レートã�®æŒ‡å®šæ–¹æ³•ã�®ã‚ªãƒ—ションã�‹ã‚‰ã€�ã�“ã�®ã‚¹ã‚¿ãƒƒã‚¯ã�®ä½œæˆ�ã�«ä½¿ç”¨ã�™" "るテンプレートã�®æŒ‡å®šæ–¹æ³•ã‚’é�¸æŠžã�—ã�¦ã��ã� ã�•ã�„。" msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "利用å�¯èƒ½ã�ªãƒ†ãƒ³ãƒ—レートã�®æŒ‡å®šæ–¹æ³•ã�®ã‚ªãƒ—ションã�‹ã‚‰ã€�ã�“ã�®ã‚¹ã‚¿ãƒƒã‚¯ã�®ãƒ—レビューã�«" "使用ã�™ã‚‹ãƒ†ãƒ³ãƒ—レートã�®æŒ‡å®šæ–¹æ³•ã‚’é�¸æŠžã�—ã�¦ã��ã� ã�•ã�„。" msgid "Version" msgstr "ãƒ�ージョン" msgid "You must specify a template via one of the available sources." msgstr "ã�„ã�šã‚Œã�‹ã�®æ–¹æ³•ã�§ãƒ†ãƒ³ãƒ—レートを指定ã�™ã‚‹å¿…è¦�ã�Œã�‚りã�¾ã�™ã€‚" msgid "environment" msgstr "環境設定" msgid "template" msgstr "テンプレート" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5024962 heat_dashboard-16.0.0/heat_dashboard/locale/ja/LC_MESSAGES/djangojs.po0000664000175000017500000000576115257164523024100 0ustar00zuulzuul# Akihiro Motoki , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Mie Yamamoto , 2016. #zanata # Yusuke Higashino , 2016. #zanata # Yuko Katabami , 2017. #zanata # Yuko Katabami , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-08-14 00:21+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-08-08 03:13+0000\n" "Last-Translator: Yuko Katabami \n" "Language-Team: Japanese\n" "Language: ja\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" msgid "Cancel" msgstr "å�–り消ã�—" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "スタックã�®ãƒ�ェック" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "スタックをãƒ�ェックã�—ã�¾ã�—ã�Ÿ: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "スタックã�®ãƒ�ェックを確定ã�™ã‚‹" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "スタックã�®å‰Šé™¤ã‚’確定ã�™ã‚‹" msgid "Create Stack" msgstr "スタックã�®ä½œæˆ�" msgid "Delete Resource" msgstr "リソースã�®å‰Šé™¤" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "スタックã�®å‰Šé™¤" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "スタック %s を削除ã�—ã�¾ã�—ã�Ÿã€‚" msgid "Download" msgstr "ダウンロード" msgid "Heat Orchestration Template" msgstr "Heat Orchestration Template" msgid "OK" msgstr "OK" msgid "Save" msgstr "ä¿�å­˜" msgid "Saved Drafts" msgstr "ドラフトã�®ä¿�å­˜" msgid "Show More Properties" msgstr "ã��ã�®ä»–ã�®ãƒ—ロパティーã�®è¡¨ç¤º" #, python-format msgid "Stack %s was successfully created." msgstr "スタック %s を作æˆ�ã�—ã�¾ã�—ã�Ÿã€‚" #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "スタックをãƒ�ェックã�Œã�§ã��ã�¾ã�›ã‚“: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "スタックを削除ã�§ã��ã�¾ã�›ã‚“: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "スタックã�®ãƒ�ェックã�¯è¨±å�¯ã�•れã�¦ã�„ã�¾ã�›ã‚“: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "スタックã�®å‰Šé™¤ã�¯è¨±å�¯ã�•れã�¦ã�„ã�¾ã�›ã‚“: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "\"%s\" ã‚’é�¸æŠžã�—ã�¾ã�—ã�Ÿã€‚" #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "\"%s\" ã‚’é�¸æŠžã�—ã�¾ã�—ã�Ÿã€‚削除ã�•れã�Ÿã‚¹ã‚¿ãƒƒã‚¯ã�¯å…ƒã�«æˆ»ã�›ã�¾ã�›ã‚“。" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.263263 heat_dashboard-16.0.0/heat_dashboard/locale/ko_KR/0000775000175000017500000000000015257164606020560 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3212616 heat_dashboard-16.0.0/heat_dashboard/locale/ko_KR/LC_MESSAGES/0000775000175000017500000000000015257164606022345 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5024962 heat_dashboard-16.0.0/heat_dashboard/locale/ko_KR/LC_MESSAGES/django.po0000664000175000017500000003407315257164523024154 0ustar00zuulzuul# Andrea Young Oak Li , 2016. #zanata # Andreas Jaeger , 2016. #zanata # Eunseop Shin , 2016. #zanata # Ian Y. Choi , 2016. #zanata # Sungjin Kang , 2016. #zanata # Ian Y. Choi , 2017. #zanata # Sungjin Kang , 2017. #zanata # johjuhyun , 2017. #zanata # minwook-shin , 2017. #zanata # ByungYeol Woo , 2018. #zanata # Ian Y. Choi , 2018. #zanata # Ian Y. Choi , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-08-02 02:26+0000\n" "Last-Translator: Ian Y. Choi \n" "Language-Team: Korean (South Korea)\n" "Language: ko_KR\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "업로드 í•  로컬 환경." msgid "A local template to upload." msgstr "업로드할 로컬 템플릿." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "템플릿ì�€ ì�¸í”„ë�¼, 서비스, 그리고 ì�‘ìš© 프로그램 ë°°í�¬ë¥¼ ìž�ë�™í™”하기 위해 사용ë�©ë‹ˆ" "다." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adopt Complete" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adopt Failed" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adopt In Progress" msgid "An external (HTTP) URL to load the template from." msgstr "템플릿ì�„ 외부 (HTTP) URL로 불러옵니다." msgid "Attributes" msgstr "특성" msgid "Case sensitive" msgstr "대소문ìž� 구별" msgid "Case-sensitive" msgstr "대소문ìž� 구별" msgid "Change Stack Template" msgstr "Stack 템플릿 변경" msgid "Change Template" msgstr "템플릿 변경" msgctxt "current status of stack" msgid "Check Complete" msgstr "확ì�¸ 완료" msgctxt "current status of stack" msgid "Check Failed" msgstr "확ì�¸ 실패" msgctxt "current status of stack" msgid "Check In Progress" msgstr "확ì�¸ ì§„í–‰ 중" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Stack 확ì�¸" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Stack 확ì�¸ë�¨" msgid "Clear Canvas" msgstr "캔버스 ì‚­ì œ" msgid "Close" msgstr "닫기" msgctxt "current status of stack" msgid "Create Complete" msgstr "ìƒ�성 완료" msgctxt "current status of stack" msgid "Create Failed" msgstr "ìƒ�성 실패" msgctxt "current status of stack" msgid "Create In Progress" msgstr "ìƒ�성 ì§„í–‰ 중" msgid "Create Stack" msgstr "Stack ìƒ�성" msgid "Create a new stack with the provided values." msgstr "작성한 ê°’ì—� 가진 새로운 스íƒ�ì�„ 만들 수 있습니다." msgid "Created" msgstr "ìƒ�성ë�¨" msgid "Creation Timeout (minutes)" msgstr "ìƒ�성 제한 시간 (ë¶„)" msgid "Date Updated" msgstr "ì—…ë�°ì�´íŠ¸í•œ ë‚ ì§œ" msgctxt "current status of stack" msgid "Delete Complete" msgstr "ì‚­ì œ 완료" msgctxt "current status of stack" msgid "Delete Failed" msgstr "ì‚­ì œ 실패" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "ì‚­ì œ ì§„í–‰ 중" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Stack ì‚­ì œ" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Stack 삭제함" msgid "Description" msgstr "설명" msgid "Description:" msgstr "설명:" msgid "Direct Input" msgstr "ì§�ì ‘ ìž…ë ¥" msgid "Disabled" msgstr "비활성화ë�¨" msgid "Edit Template" msgstr "템플릿 편집" msgid "Enable rollback on create/update failure." msgstr "ìƒ�성/ì—…ë�°ì�´íЏ 실패시 롤백 사용." msgid "Enabled" msgstr "활성화ë�¨" msgid "Environment Data" msgstr "환경 ë�°ì�´í„°" msgid "Environment File" msgstr "환경 파ì�¼" msgid "Environment Source" msgstr "환경 소스" msgid "Events" msgstr "ì�´ë²¤íЏ" msgid "Export Draft" msgstr "초안 내보내기" msgid "File" msgstr "파ì�¼" msgid "Function" msgstr "기능" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "초안 가져오기" msgctxt "current status of stack" msgid "Init Complete" msgstr "초기화 완료" msgctxt "current status of stack" msgid "Init Failed" msgstr "초기화 실패" msgctxt "current status of stack" msgid "Init In Progress" msgstr "초기화 ì§„í–‰ 중" msgid "Last Updated" msgstr "마지막 ì—…ë�°ì�´íЏ ë‚ ì§œ" msgid "Launch" msgstr "실행" msgid "Launch Parameters" msgstr "실행 매개 변수" msgid "Launch Stack" msgstr "Stack 시작" msgid "Links" msgstr "ë§�í�¬" msgid "Load Draft" msgstr "초안 불러오기" msgid "Manage Drafts" msgstr "초안 관리" msgid "Minutes" msgstr "ë¶„" msgid "Name" msgstr "ì�´ë¦„" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "ì�´ë¦„ì�€ 반드시 문ìž�로 시작ë�˜ì–´ì•¼ 하고, 오ì§� 문ìž�, 숫ìž�, ì–¸ë�”스코어(_), 마침표" "(.) ë°� 하ì�´í”ˆ(-)ë§Œ í�¬í•¨í•  수 있습니다. " msgid "Name of the stack to create." msgstr "ìƒ�성하는 Stack ì�´ë¦„입니다." msgid "Next" msgstr "다ì�Œ" msgid "Orchestration" msgstr "Orchestration" msgid "Outputs" msgstr "출력" msgid "Overview" msgstr "개요" msgid "Parameters" msgstr "매개 변수" #, python-format msgid "Password for user \"%s\"" msgstr "사용ìž� \"%s\" 비밀번호" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "ìž�ì›� ì•„ì�´ì½˜ì�„ 중앙 캔버스로 드래그 앤 드롭하여 템플릿ì�„ 위한 ìž�ì›�ì�„ ì •ì�˜í•©ë‹ˆ" "다." #, python-format msgid "Please specify a %s using only one source method." msgstr "소스 메소드 하나만 사용하여 %s 를 지정하십시오." msgid "Preview" msgstr "미리보기" msgid "Preview Stack" msgstr "Stack 미리보기" msgid "Preview Stack Details" msgstr "Stack 미리보기 ìƒ�세정보" msgid "Preview Stack Parameters" msgstr "Stack 매개 변수 미리보기" msgid "Preview Template" msgstr "템플릿 미리보기" msgid "Preview a new stack with the provided values." msgstr "제공ë�œ ê°’ì�„ 가지는 새 스íƒ�ì�„ 미리 봅니다." msgid "Properties" msgstr "ì†�성" msgid "Referenced Files" msgstr "참조ë�œ 파ì�¼ë“¤" msgid "Resource" msgstr "리소스" msgid "Resource ID" msgstr "리소스 ID" msgid "Resource Metadata" msgstr "리소스 메타ë�°ì�´í„°" msgid "Resource Types" msgstr "리소스 타입" msgid "Resources" msgstr "리소스" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Resume Complete" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Resume Failed" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Resume In Progress" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Stack 재개" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Stack 재개ë�¨" msgid "Rollback" msgstr "롤백" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "ì›�ë³µ 완료" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "ì›�ë³µ 실패" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "ì›�복진행 중" msgid "Rollback On Failure" msgstr "실패시 롤백" msgid "Save Draft" msgstr "초안 저장" msgid "Select Template" msgstr "템플릿 ì„ íƒ�" msgid "Select a new template to preview a stack." msgstr "Stackì�„ 미리 보려면 새 템플릿ì�„ ì„ íƒ�하십시오." msgid "Select a new template to re-launch a stack." msgstr "Stackì�„ 다시 시작시 새 템플릿ì�„ ì„ íƒ�합니다." msgid "Select a template to launch a stack." msgstr "Stack 시작시 템플릿ì�„ ì„ íƒ�합니다." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "스냅샷 완료" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "스냅샷 실패" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "스냅샷 ì§„í–‰ 중" msgid "Stack Events" msgstr "Stack ì�´ë²¤íЏ" msgid "Stack ID" msgstr "Stack ID" msgid "Stack ID =" msgstr "Stack ID =" msgid "Stack Name" msgstr "Stack ì�´ë¦„" msgid "Stack Name =" msgstr "Stack ì�´ë¦„ =" msgid "Stack Parameters" msgstr "스íƒ� 매개 변수" msgid "Stack Preview" msgstr "스íƒ� 미리보기" msgid "Stack Resource" msgstr "스íƒ� 리소스" msgid "Stack Resource ID" msgstr "스íƒ� 리소스 ID" msgid "Stack Resource Type" msgstr "Stack 리소스 타입" msgid "Stack Resources" msgstr "Stack 리소스" msgid "Stack creation started." msgstr "Stack ìƒ�성 중입니다." msgid "Stack creation timeout in minutes." msgstr "Stack ìƒ�성 제한 시간 (ë¶„)." msgid "Stack update started." msgstr "Stack ì—…ë�°ì�´íŠ¸ë¥¼ 시작하였습니다." msgid "Stack updating timeout in minutes." msgstr "스íƒ� ì—…ë�°ì�´íЏ 제한시간 (ë¶„)" msgid "Stacks" msgstr "Stack" msgid "Status" msgstr "ìƒ�태" msgid "Status =" msgstr "ìƒ�태 = " msgid "Status Reason" msgstr "ìƒ�태 현황" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspend Complete" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspend Failed" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspend In Progress" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Stack 중지" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Stack 중지ë�¨" msgid "Template" msgstr "템플릿" msgid "Template Data" msgstr "템플릿 ë�°ì�´í„°" msgid "Template File" msgstr "템플릿 파ì�¼" msgid "Template Functions" msgstr "템플릿 기능" msgid "Template Generator" msgstr "템플릿 ìƒ�성기" msgid "Template Source" msgstr "템플릿 소스" msgid "Template URL" msgstr "템플릿 URL" msgid "Template Version" msgstr "템플릿 버전" msgid "Template Versions" msgstr "템플릿 버전" msgid "The raw contents of the environment file." msgstr "환경 파ì�¼ raw 컨í…�트." msgid "The raw contents of the template." msgstr "템플릿 raw 내용입니다." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "%(prefix)s ì�„(를) 파싱하는ë�™ì•ˆ 문제가 ë°œìƒ�했습니다: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "ì�´ 작업ì�€ Stack ìƒ�명주기 ë�™ì•ˆ 수행해야 하는 작업ì—� 필요합니다." msgid "Time Since Event" msgstr "ì�´ë²¤íЏ ì�´í›„ 시간" msgid "Timeout" msgstr "제한시간" msgid "Topology" msgstr "토í�´ë¡œì§€" msgid "Type" msgstr "유형" msgid "Type =" msgstr "Type =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Stack \"%s\" ì—� 대한 ì�´ë²¤íŠ¸ë¥¼ 가져올 수 없습니다." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "템플릿 버전 \"%s\"ì—� 대한 기능ì�„ 찾지 못했습니다." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Stack \"%s\" ì—� 대한 리소스를 가져오지 못했습니다." msgid "Unable to retrieve metadata." msgstr "메타ë�°ì�´í„°ë¥¼ 찾지 못했습니다." msgid "Unable to retrieve resource type details." msgstr "리소스 유형 세부 정보를 찾지 못했습니다." msgid "Unable to retrieve resource." msgstr "리소스를 찾지 못했습니다." msgid "Unable to retrieve stack resource types." msgstr "Stack 리소스 유형ì�„ 찾지 못했습니다." msgid "Unable to retrieve stack template." msgstr "Stack 템플릿ì�„ 가져올 수 없습니다." msgid "Unable to retrieve stack." msgstr "Stackì�„ 찾지 못했습니다." msgid "Unable to retrieve template functions." msgstr "템플릿 기능ì�„ 찾지 못했습니다." msgid "Unable to retrieve template versions." msgstr "템플릿 버전ì�„ 찾지 못했습니다." msgid "Update" msgstr "ì—…ë�°ì�´íЏ" msgctxt "current status of stack" msgid "Update Complete" msgstr "ì—…ë�°ì�´íЏ 완료" msgctxt "current status of stack" msgid "Update Failed" msgstr "ì—…ë�°ì�´íЏ 실패" msgctxt "current status of stack" msgid "Update In Progress" msgstr "ì—…ë�°ì�´íЏ ì§„í–‰ 중" msgid "Update Stack" msgstr "Stack ì—…ë�°ì�´íЏ" msgid "Update Stack Parameters" msgstr "Stack 매개변수 ì—…ë�°ì�´íЏ" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "제공ë�œ 값으로 스íƒ�ì�„ ì—…ë�°ì�´íŠ¸í•©ë‹ˆë‹¤. 암호와 ê°™ì�€ 암호화ë�œ 매개변수를 변경하" "ì§€ 않는 경우, 기본값으로 초기화ë�˜ë‹ˆ 유ì�˜í•˜ì‹­ì‹œì˜¤." msgid "Updated" msgstr "ì—…ë�°ì�´íЏë�¨" msgid "Updating Timeout (minutes)" msgstr "ì—…ë�°ì�´íЏ 제한시간 (ë¶„)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "ì�´ 스íƒ�ì�„ 변경할 때 사용ë�  템플릿ì�„ 지정하기 위하여 사용할 수 있는 템플릿 소" "스 옵션 중 하나를 사용합니다." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "스íƒ�ì�„ 만드는ë�° 필요한 템플릿ì�„ 지정하는ë�° 사용할 수 있는 템플릿 소스 옵션 " "중 하나를 사용합니다." msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "스íƒ� 미리보기ì—�서 사용ë�˜ëŠ” 템플릿ì�€ 지정가능한 템플릿 소스중 하나를 사용합니" "다." msgid "Version" msgstr "버전" msgid "You must specify a template via one of the available sources." msgstr "사용 가능한 소스중 하나를 ì„ íƒ�하여 템필릿ì�„ 지정해야 합니다." msgid "environment" msgstr "environment" msgid "template" msgstr "template" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5024962 heat_dashboard-16.0.0/heat_dashboard/locale/ko_KR/LC_MESSAGES/djangojs.po0000664000175000017500000000553515257164523024512 0ustar00zuulzuul# Andreas Jaeger , 2016. #zanata # SeongSoo Cho , 2016. #zanata # Ian Y. Choi , 2017. #zanata # Sungjin Kang , 2017. #zanata # Sungjin Kang , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-08-14 00:21+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-07-28 03:22+0000\n" "Last-Translator: Sungjin Kang \n" "Language-Team: Korean (South Korea)\n" "Language: ko_KR\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" # auto translated by TM merge from project: magnum-ui, version: master, DocId: magnum_ui/locale/djangojs msgid "Cancel" msgstr "취소" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Stack 검사" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Stack 검사ë�¨: %s" msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Stack 검사 확ì�¸" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Stack ì‚­ì œ 확ì�¸" msgid "Create Stack" msgstr "Stack ìƒ�성" msgid "Delete Resource" msgstr "리소스 ì‚­ì œ" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Stack ì‚­ì œ" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Stack ì‚­ì œë�¨: %s" msgid "Download" msgstr "다운로드" msgid "Heat Orchestration Template" msgstr "Heat Orchestration 템플릿" msgid "OK" msgstr "OK" msgid "Save" msgstr "저장" msgid "Saved Drafts" msgstr "초안으로 저장ë�¨" msgid "Show More Properties" msgstr "추가 ì†�성 표시" #, python-format msgid "Stack %s was successfully created." msgstr "Stack %s 를 성공ì �으로 ìƒ�성하였습니다." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Stack 검사를 í•  수 없습니다: %s" #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Stack ì�„ 삭제할 수 없습니다: %s" #, python-format msgid "You are not allowed to check stacks: %s" msgstr "Stack ì�„ 검사할 수 없습니다: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "Stack 삭제를 í•  수 없습니다: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "\"%s\" 를 ì„ íƒ�했습니다." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "\"%s\"를 ì„ íƒ�했습니다. ì‚­ì œë�œ stackì�€ 복구할 수 없습니다." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.263263 heat_dashboard-16.0.0/heat_dashboard/locale/ne/0000775000175000017500000000000015257164606020155 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3212616 heat_dashboard-16.0.0/heat_dashboard/locale/ne/LC_MESSAGES/0000775000175000017500000000000015257164606021742 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5034962 heat_dashboard-16.0.0/heat_dashboard/locale/ne/LC_MESSAGES/django.po0000664000175000017500000005173315257164523023553 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Surit Aryal , 2018. #zanata # Surit Aryal , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-06-13 11:25+0000\n" "Last-Translator: Surit Aryal \n" "Language-Team: Nepali\n" "Language: ne\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "अपलोड गरà¥�नका लागि लोकल वातावरण" msgid "A local template to upload." msgstr "अपलोड गरà¥�नका लागि à¤�उटा सà¥�थानीय टेमà¥�पà¥�लेट" msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "à¤�क टेमà¥�पलेट आधारभूत संरचना, सेवाहरà¥�, र अनà¥�पà¥�रयोगहरà¥�को तैनाती सà¥�वचालित गरà¥�न को लागी पà¥�रयोग " "गरिनà¥�छ।" msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adopt Complete" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adopt Failed" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adopt In Progress" msgid "An external (HTTP) URL to load the template from." msgstr "बाट टेमà¥�पà¥�लेट लोड गरà¥�नका लागि à¤�उटा बाहà¥�य (HTTP) URL" msgid "Attributes" msgstr "विशेषताहरू" msgid "Case sensitive" msgstr "केस संवेदनशील" msgid "Case-sensitive" msgstr "केस संवेदनशील" msgid "Change Stack Template" msgstr "सà¥�टेक सà¥�टेमà¥�पà¥�लेट परिवरà¥�तन गरà¥�नà¥�होसà¥�" msgid "Change Template" msgstr "टेमà¥�पà¥�लेट परिवरà¥�तन गरà¥�नà¥�होसà¥�" msgctxt "current status of stack" msgid "Check Complete" msgstr "Check Complete" msgctxt "current status of stack" msgid "Check Failed" msgstr "Check Failed" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Check In Progress" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "सà¥�टà¥�याक जाà¤�च गरà¥�नà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू जाà¤�च गरà¥�नà¥�होसà¥�" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "जाà¤�च गरिà¤�को सà¥�टà¥�याक" msgstr[1] "जाà¤�च गरिà¤�का सà¥�टà¥�याकहरू" msgid "Clear Canvas" msgstr "खाली कà¥�यानवास" msgid "Close" msgstr "बनà¥�द" msgctxt "current status of stack" msgid "Create Complete" msgstr "सिरà¥�जना समà¥�पनà¥�न भयो" msgctxt "current status of stack" msgid "Create Failed" msgstr "सिरà¥�जना असफल भयो" msgctxt "current status of stack" msgid "Create In Progress" msgstr "सिरà¥�जना हà¥�दैछ" msgid "Create Stack" msgstr "सà¥�टेक बनाउनà¥�होसà¥�" msgid "Create a new stack with the provided values." msgstr "पà¥�रदान गरिà¤�का मानहरूको साथ नयाà¤� सà¥�टà¥�याक सिरà¥�जना गरà¥�नà¥�होसà¥�।" msgid "Created" msgstr "सृषà¥�टि भयो" msgid "Creation Timeout (minutes)" msgstr "बनाउने समय सकियो( मिनटहरू)" msgid "Date Updated" msgstr "तारिक अपडेट गरिà¤�को छ" msgctxt "current status of stack" msgid "Delete Complete" msgstr "मेटाईयो" msgctxt "current status of stack" msgid "Delete Failed" msgstr "मेटà¥�न असफल भयो" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "मेटाउà¤�दै" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "सà¥�टà¥�याक मेटाउनà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू मेटाउनà¥�होसà¥�" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "मेटिà¤�को सà¥�टà¥�याक" msgstr[1] "मेटिà¤�को सà¥�टà¥�याकहरू" msgid "Description" msgstr "विबरण" msgid "Description:" msgstr "विवरण:" msgid "Direct Input" msgstr "सिधा इनपà¥�ट" msgid "Disabled" msgstr "निसà¥�कà¥�रीय गरिà¤�को" msgid "Edit Template" msgstr "टेमà¥�पà¥�लेट समà¥�पादन गरà¥�नà¥�होसà¥�" msgid "Enable rollback on create/update failure." msgstr "बनाउनà¥�/अपडेट असफलतामा फरà¥�किनà¥� सकà¥�रीय गरà¥�नà¥�होसà¥�।" msgid "Enabled" msgstr "सकà¥�रीय गरियो" msgid "Environment Data" msgstr "वातावरण डाटा" msgid "Environment File" msgstr "वातावरण फाइल" msgid "Environment Source" msgstr "वातावरण सà¥�रोत" msgid "Events" msgstr "इभेनà¥�टहरू" msgid "Export Draft" msgstr "डà¥�राफà¥�ट निरà¥�यात गरà¥�नà¥�होसà¥�" msgid "File" msgstr "फाइल" msgid "Function" msgstr "पà¥�रकारà¥�य" msgid "ID" msgstr "आई डि" msgid "Import Draft" msgstr "डà¥�राफà¥�ट आयात गरà¥�नà¥�होसà¥�" msgctxt "current status of stack" msgid "Init Complete" msgstr "Init Complete" msgctxt "current status of stack" msgid "Init Failed" msgstr "Init Failed" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Init In Progress" msgid "Last Updated" msgstr "अघिलà¥�लोपलà¥�ट अपडेट गरिà¤�को " msgid "Launch" msgstr "शà¥�रू गरà¥�नà¥�होसà¥�" msgid "Launch Parameters" msgstr "पà¥�यारामिटर सà¥�रू गरà¥�नà¥�होसà¥�" msgid "Launch Stack" msgstr "सà¥�टेक आरमà¥�भ गरà¥�नà¥�होसà¥�" msgid "Links" msgstr "लिङà¥�कहरू" msgid "Load Draft" msgstr "डà¥�राफà¥�ट लोड गरà¥�नà¥�होसà¥�" msgid "Manage Drafts" msgstr "डà¥�राफà¥�टहरू वà¥�यवसà¥�थापन गरà¥�नà¥�होसà¥�" msgid "Minutes" msgstr "मिनेट" msgid "Name" msgstr "नाम" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "नाम आवशà¥�यक रूपमा अकà¥�षरबाट थालिनà¥�परà¥�छ अनि यसमा अकà¥�षर, सङà¥�खà¥�या, अनà¥�डरसà¥�कोर, पिरियडहूर अनि " "हाइफनहरू मातà¥�र हà¥�नà¥�परà¥�छ।" msgid "Name of the stack to create." msgstr "बनाउनà¥�परà¥�ने सà¥�टेकको नाम।" msgid "Next" msgstr "अरà¥�को" msgid "Orchestration" msgstr "अरà¥�केसà¥�टà¥�रेसन" msgid "Outputs" msgstr "निरà¥�गतहरू" msgid "Overview" msgstr "ओभरभà¥�यà¥�" msgid "Parameters" msgstr "पà¥�यारामिटर" #, python-format msgid "Password for user \"%s\"" msgstr "उपभोकà¥�ता \"%s\"को लागि पासवरà¥�ड" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "कृपया टेमà¥�पलेटको संसाधन परिभाषित गरà¥�नको लागी केनà¥�दà¥�र कà¥�यानवासमा सà¥�रोत आइकनहरू डà¥�रà¥�याग " "गरà¥�नà¥�होसà¥� र डà¥�रप गरà¥�नà¥�होसà¥�।" #, python-format msgid "Please specify a %s using only one source method." msgstr "à¤�उटा मातà¥�र सà¥�रोत बिधि पà¥�रयोग गरेर कृपया à¤�उटा %s तोकà¥�नà¥�होसà¥�" msgid "Preview" msgstr "पूरà¥�वावलोकन" msgid "Preview Stack" msgstr "सà¥�टà¥�याक पूरà¥�वावलोकन गरà¥�नà¥�होसà¥�" msgid "Preview Stack Details" msgstr "सà¥�टà¥�याक विवरणहरू पूरà¥�वावलोकन गरà¥�नà¥�होसà¥�" msgid "Preview Stack Parameters" msgstr "सà¥�टà¥�याक पà¥�यारामिटरहरू पूरà¥�वावलोकन गरà¥�नà¥�होसà¥�" msgid "Preview Template" msgstr "टेमà¥�पà¥�लेट पूरà¥�वावलोकन गरà¥�नà¥�होसà¥�" msgid "Preview a new stack with the provided values." msgstr "पà¥�रदान गरिà¤�का मानहरूको साथ नयाà¤� सà¥�टà¥�याक सिरà¥�जना गरà¥�नà¥�होसà¥�।" msgid "Properties" msgstr "गà¥�णहरू" msgid "Referenced Files" msgstr "सनà¥�दरà¥�भ गरिà¤�का फाइलहरू" msgid "Resource" msgstr "सामगà¥�री" msgid "Resource ID" msgstr "रिसोरà¥�स ID" msgid "Resource Metadata" msgstr "रिसोरà¥�स मेटाडाटा" msgid "Resource Types" msgstr "सà¥�रोत पà¥�रकारहरू" msgid "Resources" msgstr "रिसोरà¥�सहरू" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Resume Complete" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Resume Failed" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Resume In Progress" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "सà¥�टà¥�याक पà¥�न: सà¥�रà¥� गरà¥�नà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू पà¥�न: सà¥�रà¥� गरà¥�नà¥�होसà¥�" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "पà¥�न: सà¥�रà¥� भà¤�को सà¥�टà¥�याक" msgstr[1] "पà¥�न: सà¥�रà¥� भà¤�को सà¥�टà¥�याकहरू" msgid "Rollback" msgstr "पछि फरà¥�किनà¥�" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Rollback Complete" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Rollback Failed" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Rollback In Progress" msgid "Rollback On Failure" msgstr "असफलतामा फरà¥�किनà¥�होसà¥�" msgid "Save Draft" msgstr "डà¥�राफà¥�ट बचत गरà¥�नà¥�होसà¥�" msgid "Select Template" msgstr "टेमà¥�पà¥�लेट चयन गरà¥�नà¥�होसà¥�" msgid "Select a new template to preview a stack." msgstr "सà¥�टà¥�याक पूरà¥�वावलोकन गरà¥�न नयाà¤� टेमà¥�पà¥�लेट चयन गरà¥�नà¥�होसà¥�।" msgid "Select a new template to re-launch a stack." msgstr "à¤�उटा सà¥�टेक पà¥�नःआरमà¥�भ गरà¥�नका लागि à¤�उटा नयाà¤� टेमà¥�पà¥�लेट चन गरà¥�नà¥�होसà¥�।" msgid "Select a template to launch a stack." msgstr "à¤�उटा सà¥�टेक आरमà¥�भ गरà¥�नका लागि à¤�उटा टेमà¥�पलेट चयन गरà¥�नà¥�होसà¥�।" msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Snapshot Complete" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Snapshot Failed" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Snapshot In Progress" msgid "Stack Events" msgstr "सà¥�टेक इभेनà¥�टहरू" msgid "Stack ID" msgstr "सà¥�टेक ID" msgid "Stack ID =" msgstr "सà¥�टà¥�याक आईडी =" msgid "Stack Name" msgstr "सà¥�टेक नाम" msgid "Stack Name =" msgstr "सà¥�टà¥�याक नाम =" msgid "Stack Parameters" msgstr "सà¥�टà¥�याक पà¥�यारामिटर" msgid "Stack Preview" msgstr "सà¥�टà¥�याक पूरà¥�वावलोकन" msgid "Stack Resource" msgstr "सà¥�टेक सà¥�रोत" msgid "Stack Resource ID" msgstr "सà¥�टà¥�याक रिसोरà¥�स ID" msgid "Stack Resource Type" msgstr "सà¥�टेक रिसोरà¥�स पà¥�रकार" msgid "Stack Resources" msgstr "सà¥�टेक रिसोरà¥�सहरू" msgid "Stack creation started." msgstr "सà¥�टेक बनाउने शà¥�रू गरिà¤�को छ।" msgid "Stack creation timeout in minutes." msgstr "मिनटमा सà¥�टेक बनाउने समय सकियो।" msgid "Stack update started." msgstr "सà¥�टेक अपडेट शà¥�रू गरिà¤�को छ।" msgid "Stack updating timeout in minutes." msgstr "मिनेटमा समयसिमा अदà¥�यावधिक थà¥�पारà¥�नà¥�होसà¥�।" msgid "Stacks" msgstr "सà¥�टेकहरू" msgid "Status" msgstr "सà¥�टाटस" msgid "Status =" msgstr "सà¥�टाटस =" msgid "Status Reason" msgstr "सà¥�टाटस कारण" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspend Complete" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspend Failed" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspend In Progress" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "सà¥�टà¥�याक निलमà¥�बन गरà¥�नà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू निलमà¥�बन गरà¥�नà¥�होसà¥�" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "निलमà¥�बित सà¥�टà¥�याक" msgstr[1] "निलमà¥�बित सà¥�टà¥�याकहरू" msgid "Template" msgstr "टेमà¥�पà¥�लेट" msgid "Template Data" msgstr "टेमà¥�पà¥�लेट डाटा" msgid "Template File" msgstr "टेमà¥�पà¥�लेट फाइल" msgid "Template Functions" msgstr "टेमà¥�पà¥�लेट पà¥�रकारà¥�यहरू" msgid "Template Generator" msgstr "टेमà¥�पलेट सरà¥�जक" msgid "Template Source" msgstr "टेमà¥�पà¥�लेट सà¥�रोत" msgid "Template URL" msgstr "टेमà¥�पà¥�लेट URL" msgid "Template Version" msgstr "टेमà¥�पà¥�लेट संसà¥�करण" msgid "Template Versions" msgstr "टेमà¥�पलेट संसà¥�करणहरू" msgid "The raw contents of the environment file." msgstr "वातावरण फाइलको काà¤�चो कनà¥�टेनà¥�टहरू।" msgid "The raw contents of the template." msgstr "टेमà¥�पलेटको काà¤�चो सामगà¥�री" #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "%(prefix)s: %(error)s पारà¥�सिङ गरà¥�ने कà¥�रममा समसà¥�या भà¤�को थियो" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "यो सà¥�टेकको समà¥�पूरà¥�ण जीवनकà¥�रममा कारà¥�यानà¥�वयन गरà¥�ने अभियानका लागि आवसà¥�यक परà¥�दछ।" msgid "Time Since Event" msgstr "घटनादेखिको समय" msgid "Timeout" msgstr "समय सकियो" msgid "Topology" msgstr "टोपोलोजी" msgid "Type" msgstr "विवरण" msgid "Type =" msgstr "पà¥�रकार =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "\"%s\" सà¥�टेकका लागि इभेनà¥�टहरू पà¥�रापà¥�त गरà¥�न असमरà¥�थ" #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "टेमà¥�पà¥�लेट संसà¥�करण \"%s\" को लागि कारà¥�यहरू पà¥�रापà¥�त गरà¥�न असकà¥�षम।" #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "\"%s\" सà¥�टेकका लागि रिसोरà¥�सहरू पà¥�रापà¥�त गरà¥�न असमरà¥�थ" msgid "Unable to retrieve metadata." msgstr "मेटाडाटा पà¥�रापà¥�त गरà¥�न असमरà¥�थ।" msgid "Unable to retrieve resource type details." msgstr "संसाधन पà¥�रकारको जानकारी पà¥�न: पà¥�रापà¥�त गरà¥�न असकà¥�षम।" msgid "Unable to retrieve resource." msgstr "रिसोरà¥�स पà¥�रापà¥�त गरà¥�न असमरà¥�थ" msgid "Unable to retrieve stack resource types." msgstr "सà¥�टà¥�याक संसाधन पà¥�रकारहरू पà¥�नःपà¥�रापà¥�त गरà¥�न असमरà¥�थ।" msgid "Unable to retrieve stack template." msgstr "सà¥�टेक टेमà¥�पà¥�लेट पà¥�रापà¥�त गरà¥�न असमरà¥�थ" msgid "Unable to retrieve stack." msgstr "सà¥�टेक पà¥�रापà¥�त गरà¥�न असमरà¥�थ" msgid "Unable to retrieve template functions." msgstr "टेमà¥�पà¥�लेट पà¥�रकारà¥�यहरू पà¥�न: पà¥�रापà¥�त गरà¥�न असमरà¥�थ।" msgid "Unable to retrieve template versions." msgstr "टेमà¥�पà¥�लेट संसà¥�करणहरू पà¥�न: पà¥�रापà¥�त गरà¥�न असमरà¥�थ।" msgid "Update" msgstr "अपडेट गरà¥�नà¥�होसà¥�" msgctxt "current status of stack" msgid "Update Complete" msgstr "अदà¥�यावधिक समà¥�पनà¥�न भयो" msgctxt "current status of stack" msgid "Update Failed" msgstr "अदà¥�यावधिक असफल भयो" msgctxt "current status of stack" msgid "Update In Progress" msgstr "अदà¥�यावधिक हà¥�दैछ" msgid "Update Stack" msgstr "सà¥�टेक अपडेट गरà¥�नà¥�होसà¥�" msgid "Update Stack Parameters" msgstr "सà¥�टेक पà¥�यारामिटरहरू अपडेट गरà¥�नà¥�होसà¥�" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "पà¥�रदान गरिà¤�का मूलà¥�यहरू लिà¤�र à¤�उटा सà¥�टेक अपडेट गरà¥�नà¥�होसà¥�। कृपय नोट गरà¥�नà¥�होसà¥� कि कà¥�ना इनà¥�कà¥�रिपà¥�ट " "गरिà¤�को पà¥�यारामिटर, जसà¥�तै पासवरà¥�डहरू, यदि तपाईà¤�ले परिवरà¥�तन गरà¥�नà¥�भà¤�न भने डिफलà¥�टमा रिसेट हà¥�नà¥�छ।" msgid "Updated" msgstr "अपडेट गरिà¤�को" msgid "Updating Timeout (minutes)" msgstr "समय समापà¥�त (मिनेट) अदà¥�यावधिक गरà¥�ने" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "यो सà¥�टà¥�याक परिवरà¥�तन गरà¥�न टेमà¥�पà¥�लेट निरà¥�दिषà¥�ट गरà¥�न उपलबà¥�ध टेमà¥�पà¥�लेट सà¥�रोत विकलà¥�प मधà¥�ये à¤�क पà¥�रयोग " "गरà¥�नà¥�होसà¥�।" msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "सà¥�टेक बनाउने कà¥�रममा पà¥�रयोग गरà¥�नका लागि टेमà¥�पà¥�लेक पà¥�रयोग गरà¥�नका लागि उपलबà¥�ध कà¥�नै à¤�क टेमà¥�पà¥�ले " "सà¥�रोतको पà¥�रयोग गरà¥�नà¥�होसà¥�।" msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "यो सà¥�टà¥�याक पूरà¥�वावलोकनमा पà¥�रयोग हà¥�ने टेमà¥�पलेट निरà¥�दिषà¥�ट गरà¥�न उपलबà¥�ध टेमà¥�पà¥�लेट सà¥�रोत विकलà¥�प मधà¥�ये " "à¤�क पà¥�रयोग गरà¥�नà¥�होसà¥�।" msgid "Version" msgstr "संसà¥�करण" msgid "You must specify a template via one of the available sources." msgstr "उपलबà¥�ध सà¥�रोतहरूमधà¥�ये à¤�उटाबाट तपाईà¤�ले आवशà¥�यक रूपमा à¤�उटा टेमà¥�पà¥�लेट निशà¥�चित गरà¥�नà¥�परà¥�छ।" msgid "environment" msgstr "वातावरण" msgid "template" msgstr "टमà¥�पà¥�लेट" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5034962 heat_dashboard-16.0.0/heat_dashboard/locale/ne/LC_MESSAGES/djangojs.po0000664000175000017500000001064015257164523024100 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Surit Aryal , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-05-22 15:39+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-06-14 04:55+0000\n" "Last-Translator: Surit Aryal \n" "Language-Team: Nepali\n" "Language: ne\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "Cancel" msgstr "रदà¥�ध गरà¥�नà¥�होसà¥�" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "सà¥�टà¥�याक जाà¤�च गरà¥�नà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू जाà¤�च गरà¥�नà¥�होसà¥�" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "जाà¤�च गरिà¤�को सà¥�टà¥�याक: %s।" msgstr[1] "जाà¤�च गरिà¤�को सà¥�टà¥�याकहरू: %s।" msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "सà¥�टà¥�याक जाà¤�च पà¥�षà¥�टि गरà¥�नà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू जाà¤�च पà¥�षà¥�टि गरà¥�नà¥�होसà¥�" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "सà¥�टà¥�याक मेटाउनà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू मेटाउनà¥�होसà¥�" msgid "Create Stack" msgstr "सà¥�टेक बनाउनà¥�होसà¥�" msgid "Delete Resource" msgstr "सामगà¥�री मेटाउनà¥�होसà¥�" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "सà¥�टà¥�याक मेटाउनà¥�होसà¥�" msgstr[1] "सà¥�टà¥�याकहरू मेटाउनà¥�होसà¥�" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "मेटिà¤�को सà¥�टà¥�याक %s" msgstr[1] "मेटिà¤�को सà¥�टà¥�याकहरू %s" msgid "Download" msgstr "डाउनलोड गरà¥�नà¥�होसà¥�" msgid "Heat Orchestration Template" msgstr "Heat Orchestration टमà¥�पà¥�लेट" msgid "OK" msgstr "ठिक छ" msgid "Save" msgstr "सेभ गरà¥�नà¥�होसà¥�" msgid "Saved Drafts" msgstr "सà¥�रकà¥�षित डà¥�राफà¥�टहरू" msgid "Show More Properties" msgstr "अधिक गà¥�णहरू देखाउनà¥�होसà¥�" #, python-format msgid "Stack %s was successfully created." msgstr "सà¥�टà¥�याक %s सफलतापूरà¥�वक सिरà¥�जना गरियो।" #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "%s सà¥�टà¥�याक जाà¤�च गरà¥�न सकिà¤�न।" msgstr[1] "%s सà¥�टà¥�याकहरू जाà¤�च गरà¥�न सकिà¤�न।" #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "सà¥�टà¥�याक: %s मेटाउन असमरà¥�थ।" msgstr[1] "सà¥�टà¥�याकहरू: %s मेटाउन असमरà¥�थ।" #, python-format msgid "You are not allowed to check stacks: %s" msgstr "तपाईंलाई सà¥�टà¥�याक: %s जाà¤�च गरà¥�न अनà¥�मति छैन" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "तपाईंलाई सà¥�टà¥�याक: %s जाà¤�च गरà¥�न अनà¥�मति छैन" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "तपाईंले \"%s\".चयन गरà¥�नà¥�भà¤�को छ।" msgstr[1] "तपाईंले \"%s\".चयन गरà¥�नà¥�भà¤�को छ।" #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "तपाईंले \"%s\" चयन गरà¥�नà¥�भà¤�को छ। हटाइà¤�को सà¥�टà¥�याक पà¥�न: पà¥�रापà¥�ति योगà¥�य छैन।" msgstr[1] "तपाईंले \"%s\" चयन गरà¥�नà¥�भà¤�को छ। हटाइà¤�को सà¥�टà¥�याकहरू पà¥�न: पà¥�रापà¥�ति योगà¥�य छैन।" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.264263 heat_dashboard-16.0.0/heat_dashboard/locale/pl_PL/0000775000175000017500000000000015257164606020561 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3212616 heat_dashboard-16.0.0/heat_dashboard/locale/pl_PL/LC_MESSAGES/0000775000175000017500000000000015257164606022346 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.504496 heat_dashboard-16.0.0/heat_dashboard/locale/pl_PL/LC_MESSAGES/django.po0000664000175000017500000002330015257164523024144 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Å�ukasz Jernas , 2015. #zanata # Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-04-12 06:07+0000\n" "Last-Translator: Copied by Zanata \n" "Language-Team: Polish (Poland)\n" "Language: pl_PL\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2)\n" msgid "A local environment to upload." msgstr "Lokalne Å›rodowisko do przesÅ‚ania." msgid "A local template to upload." msgstr "Lokalny plik z szablonem do wysÅ‚ania." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "ZakoÅ„czono adoptowanie" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Adoptowanie nieudane" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Trwa adoptowanie" msgid "An external (HTTP) URL to load the template from." msgstr "ZewnÄ™trzny adres URL (HTTP), z którego należy pobrać szablon." msgid "Change Stack Template" msgstr "ZmieÅ„ szablon stosu" msgid "Change Template" msgstr "ZmieÅ„ szablon" msgctxt "current status of stack" msgid "Check Complete" msgstr "ZakoÅ„czono sprawdzanie" msgctxt "current status of stack" msgid "Check Failed" msgstr "Sprawdzanie nieudane" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Trwa sprawdzanie" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Sprawdź stos" msgstr[1] "Sprawdź stosy" msgstr[2] "Sprawdź stosy" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Sprawdzono stos" msgstr[1] "Sprawdzono stosy" msgstr[2] "Sprawdzono stosy" msgctxt "current status of stack" msgid "Create Complete" msgstr "ZakoÅ„czono tworzenie" msgctxt "current status of stack" msgid "Create Failed" msgstr "Tworzenie nieudane" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Trwa tworzenie" msgid "Create Stack" msgstr "Utwórz stos" msgid "Created" msgstr "Utworzono" msgid "Creation Timeout (minutes)" msgstr "Limit czasu tworzenia (w minutach)" msgid "Date Updated" msgstr "Data aktualizacji" msgctxt "current status of stack" msgid "Delete Complete" msgstr "ZakoÅ„czono usuwanie" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Nieudane usuniÄ™cie" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Trwa usuwanie" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "UsuÅ„ stos" msgstr[1] "UsuÅ„ stosy" msgstr[2] "UsuÅ„ stosy" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "UsuniÄ™to stos" msgstr[1] "UsuniÄ™to stosy" msgstr[2] "UsuniÄ™to stosy" msgid "Description" msgstr "Opis" msgid "Direct Input" msgstr "BezpoÅ›rednio" msgid "Edit Template" msgstr "Edycja szablonu" msgid "Enable rollback on create/update failure." msgstr "Wycofuje zmiany, jeÅ›li wystÄ…pi błąd podczas tworzenia lub modyfikacji." msgid "Environment Data" msgstr "Dane Å›rodowiska" msgid "Environment File" msgstr "Plik Å›rodowiska" msgid "Environment Source" msgstr "ŹródÅ‚o Å›rodowiska" msgid "Events" msgstr "Zdarzenia" msgid "File" msgstr "Plik" msgctxt "current status of stack" msgid "Init Complete" msgstr "ZakoÅ„czono inicjowanie" msgctxt "current status of stack" msgid "Init Failed" msgstr "Inicjowanie nieudane" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Trwa inicjowanie" msgid "Launch" msgstr "Uruchom" msgid "Launch Stack" msgstr "Uruchom stos" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "Nazwa może zawierać wyłącznie litery, cyfry, kropki, myÅ›lniki i znaki " "podkreÅ›lenia." msgid "Name of the stack to create." msgstr "Nazwa stosu, który zostanie utworzony." msgid "Next" msgstr "NastÄ™pny" msgid "Orchestration" msgstr "Orkiestracja" msgid "Overview" msgstr "PrzeglÄ…d" #, python-format msgid "Password for user \"%s\"" msgstr "HasÅ‚o dla użytkownika „%s" #, python-format msgid "Please specify a %s using only one source method." msgstr "Należy podać %s wykorzystujÄ…c tylko jednÄ… metodÄ™ źródÅ‚owÄ…." msgid "Preview" msgstr "PodglÄ…d" msgid "Preview Stack" msgstr "PoglÄ…d stosu" msgid "Preview Stack Details" msgstr "PodglÄ…d szczegółów stosu" msgid "Preview Stack Parameters" msgstr "Podejrzyj parametry stosu" msgid "Preview Template" msgstr "PodglÄ…d szablonu" msgid "Resource" msgstr "Zasób" msgid "Resource Types" msgstr "Typy zasobów" msgid "Resources" msgstr "Zasoby" msgctxt "current status of stack" msgid "Resume Complete" msgstr "ZakoÅ„czono wznawianie" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Wznawianie nieudane" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Trwa wznawianie" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Wznów stos" msgstr[1] "Wznów stosy" msgstr[2] "Wznów stosy" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Wznowiono stos" msgstr[1] "Wznowiono stosy" msgstr[2] "Wznowiono stosy" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "ZakoÅ„czono przywracanie" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Przywracanie nieudane" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Trwa przywracanie" msgid "Rollback On Failure" msgstr "Wycofanie po wystÄ…pieniu błędu" msgid "Select Template" msgstr "Wybór szablonu" msgid "Select a new template to preview a stack." msgstr "Należy wybrać nowy szablon, aby podejrzeć stos." msgid "Select a new template to re-launch a stack." msgstr "Należy wybrać szablon do ponownego uruchomienia stosu." msgid "Select a template to launch a stack." msgstr "Należy wybrać szablon do uruchomienia stosu." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "ZakoÅ„czono tworzenie migawki" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Tworzenie migawki nieudane" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Trwa tworzenie migawki" msgid "Stack Events" msgstr "Zdarzenia stosu" msgid "Stack ID" msgstr "ID stosu" msgid "Stack Name" msgstr "Nazwa stosu" msgid "Stack Resource" msgstr "Zasób stosu" msgid "Stack Resource Type" msgstr "Typ zasobu stosu" msgid "Stack Resources" msgstr "Zasoby stosu" msgid "Stack creation started." msgstr "Tworzenie stosu." msgid "Stack creation timeout in minutes." msgstr "Limit czasu tworzenia stosu w minutach." msgid "Stack update started." msgstr "Aktualizacja stosu rozpoczÄ™ta." msgid "Stacks" msgstr "Stosy" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Stan =" msgid "Status Reason" msgstr "Powód stanu" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "ZakoÅ„czono usypianie" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Usypanie nieudane" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Trwa usypianie" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Wstrzymaj stos" msgstr[1] "Wstrzymaj stosy" msgstr[2] "Wstrzymaj stosy" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Wstrzymano stos" msgstr[1] "Wstrzymano stosy" msgstr[2] "Wstrzymano stosy" msgid "Template" msgstr "Szablon" msgid "Template Data" msgstr "Dane szablonu" msgid "Template File" msgstr "Plik szablonu" msgid "Template Source" msgstr "ŹródÅ‚o szablonu" msgid "Template URL" msgstr "URL szablonu" msgid "The raw contents of the environment file." msgstr "Zawartość pliku Å›rodowiska." msgid "The raw contents of the template." msgstr "Zawartość szablonu." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "WystÄ…piÅ‚ problem podczas przetwarzania %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Wymagane dla czynnoÅ›ci przeprowadzanych podczas caÅ‚ego cyklu życia stosu" msgid "Time Since Event" msgstr "Czas od zdarzenia" msgid "Topology" msgstr "Topologia" msgid "Type" msgstr "Typ" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Nie można pobrać zdarzeÅ„ dla stosu „%sâ€�." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Nie można pobrać zasobów dla stosu „%sâ€�." msgid "Unable to retrieve metadata." msgstr "Nie można pobrać metadanych." msgid "Unable to retrieve resource type details." msgstr "Nie można pobrać szczegółów typu zasobu." msgid "Unable to retrieve resource." msgstr "Nie można pobrać zasobu." msgid "Unable to retrieve stack resource types." msgstr "Nie można pobrać typów zasobów stosu." msgid "Unable to retrieve stack template." msgstr "Nie można pobrać szablonu stosu." msgid "Unable to retrieve stack." msgstr "Nie można pobrać stosu." msgid "Update" msgstr "Aktualizuj" msgctxt "current status of stack" msgid "Update Complete" msgstr "ZakoÅ„czono aktualizacjÄ™" msgctxt "current status of stack" msgid "Update Failed" msgstr "Aktualizacja nieudana" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Trwa aktualizacja" msgid "Update Stack" msgstr "Zaktualizuj stos" msgid "Update Stack Parameters" msgstr "Aktualizacja parametrów stosu" msgid "Updated" msgstr "Zaktualizowano" msgid "Version" msgstr "Wersja" msgid "You must specify a template via one of the available sources." msgstr "Należy podać szablon wykorzystujÄ…c jedno z dostÄ™pnych źródeÅ‚." msgid "environment" msgstr "Å›rodowisko" msgid "template" msgstr "szablon" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.264263 heat_dashboard-16.0.0/heat_dashboard/locale/pt_BR/0000775000175000017500000000000015257164606020561 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3222616 heat_dashboard-16.0.0/heat_dashboard/locale/pt_BR/LC_MESSAGES/0000775000175000017500000000000015257164606022346 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.505496 heat_dashboard-16.0.0/heat_dashboard/locale/pt_BR/LC_MESSAGES/django.po0000664000175000017500000003427715257164523024163 0ustar00zuulzuul# Carlos Marques , 2016. #zanata # José Mello , 2016. #zanata # Fernando Pimenta , 2017. #zanata # José Mello , 2017. #zanata # Marcio , 2017. #zanata # Fernando Pimenta , 2018. #zanata # Rodrigo Loures , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-06-19 11:07+0000\n" "Last-Translator: Fernando Pimenta \n" "Language-Team: Portuguese (Brazil)\n" "Language: pt_BR\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "Um ambiente local para enviar." msgid "A local template to upload." msgstr "Um modelo local para enviar." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "Um modelo é utilizado para automatizar a implantação de infraestrutura, " "serviços, e aplicações." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Adoção Concluída" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "A Adoção Falhou" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Adoção em Progresso" msgid "An external (HTTP) URL to load the template from." msgstr "Uma URL (HTTP) externa da qual carregar o modelo." msgid "Attributes" msgstr "Atributos" msgid "Case sensitive" msgstr "Diferencia maiúsculas de minúsculas" msgid "Case-sensitive" msgstr "Diferencia maiúsculas de minúsculas" msgid "Change Stack Template" msgstr "Alterar Modelo de Pilha" msgid "Change Template" msgstr "Alterar Modelo" msgctxt "current status of stack" msgid "Check Complete" msgstr "Verificação completa" msgctxt "current status of stack" msgid "Check Failed" msgstr "Falha na verificaçao" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Verificação em Progresso" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Verificar Pilha" msgstr[1] "Verificar Pilhas" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Pilha Verificada" msgstr[1] "Pilhas Verificadas" msgid "Clear Canvas" msgstr "Limpar Tela" msgid "Close" msgstr "Fechar" msgctxt "current status of stack" msgid "Create Complete" msgstr "Criação Concluída" msgctxt "current status of stack" msgid "Create Failed" msgstr "A Criação Falhou" msgctxt "current status of stack" msgid "Create In Progress" msgstr "Criação em Andamento" msgid "Create Stack" msgstr "Criar Pilha" msgid "Create a new stack with the provided values." msgstr "Criar uma nova pilha com os valores fornecidos." msgid "Created" msgstr "Criado" msgid "Creation Timeout (minutes)" msgstr "Tempo Limite de Criação (minutos)" msgid "Date Updated" msgstr "Data Atualizada" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Exclusão Concluída" msgctxt "current status of stack" msgid "Delete Failed" msgstr "A Exclusão Falhou" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Exclusão em Andamento" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Excluir Pilha" msgstr[1] "Excluir Pilhas" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Pilha Excluída" msgstr[1] "Pilhas Excluídas" msgid "Description" msgstr "Descrição" msgid "Description:" msgstr "Descrição:" msgid "Direct Input" msgstr "Entrada Direta" msgid "Disabled" msgstr "Desativado" msgid "Edit Template" msgstr "Editar Modelo" msgid "Enable rollback on create/update failure." msgstr "Habilita rollback em caso de falha de criação/atualização." msgid "Enabled" msgstr "Ativado" msgid "Environment Data" msgstr "Dados de Ambiente" msgid "Environment File" msgstr "Arquivo de Ambiente" msgid "Environment Source" msgstr "Ambiente de Origem" msgid "Events" msgstr "Eventos" msgid "Export Draft" msgstr "Exportar Rascunho" msgid "File" msgstr "Arquivo" msgid "Function" msgstr "Função" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "Importar Rascunho" msgctxt "current status of stack" msgid "Init Complete" msgstr "Inicialização Concluída" msgctxt "current status of stack" msgid "Init Failed" msgstr "A Inicialização Falhou" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Inicialização em Progresso" msgid "Last Updated" msgstr "Última Atualização" msgid "Launch" msgstr "Ativar" msgid "Launch Parameters" msgstr "Parâmetros de Lançamento" msgid "Launch Stack" msgstr "Disparar Pilha" msgid "Links" msgstr "Links" msgid "Load Draft" msgstr "Carregar Rascunho" msgid "Manage Drafts" msgstr "Gerenciar Rascunhos" msgid "Minutes" msgstr "Minutos" msgid "Name" msgstr "Nome" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "O Nome deve começar com uma letra e conter apenas letras, números, " "underscore, ponto e hífen." msgid "Name of the stack to create." msgstr "Nome da pilha a ser criada." msgid "Next" msgstr "Avançar" msgid "Orchestration" msgstr "Orquestração" msgid "Outputs" msgstr "Saídas" msgid "Overview" msgstr "Visão Geral" msgid "Parameters" msgstr "Parâmetros" #, python-format msgid "Password for user \"%s\"" msgstr "Senha para o usuário \"%s\"" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "Por favor, arraste e solte ícones de recursos na tela central para definir o " "recurso para o modelo." #, python-format msgid "Please specify a %s using only one source method." msgstr "Especifique um %s utilizando apenas um método de origem." msgid "Preview" msgstr "Pré-Visualização." msgid "Preview Stack" msgstr "Visualizar Pilha" msgid "Preview Stack Details" msgstr "Visualização de Detalhes da Pilha" msgid "Preview Stack Parameters" msgstr "Visualização de Parâmetros de Pilha" msgid "Preview Template" msgstr "Visualização do Modelo" msgid "Preview a new stack with the provided values." msgstr "Pré-visualizar uma nova pilha com os valores fornecidos." msgid "Properties" msgstr "Propriedades" msgid "Referenced Files" msgstr "Arquivos referenciados" msgid "Resource" msgstr "Recurso" msgid "Resource ID" msgstr "ID do Recurso" msgid "Resource Metadata" msgstr "Metadado do Recurso" msgid "Resource Types" msgstr "Tipos de Recursos" msgid "Resources" msgstr "Recursos" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Retomada Concluída" msgctxt "current status of stack" msgid "Resume Failed" msgstr "A Retomada Falhou" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Retomada em Progresso" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Retomar Pilha" msgstr[1] "Retomar Pilhas" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Pilha Retomada" msgstr[1] "Pilhas Retomadas" msgid "Rollback" msgstr "Reverter" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Rollback Concluído." msgctxt "current status of stack" msgid "Rollback Failed" msgstr "O Rollback Falhou" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Rollback em Progresso" msgid "Rollback On Failure" msgstr "Rollback em caso de falha" msgid "Save Draft" msgstr "Salvar Rascunho" msgid "Select Template" msgstr "Escolha o Modelo" msgid "Select a new template to preview a stack." msgstr "Selecione um novo modelo para visualizar uma pilha." msgid "Select a new template to re-launch a stack." msgstr "Selecione um novo modelo para reativar uma pilha." msgid "Select a template to launch a stack." msgstr "Selecione um modelo para disparar uma pilha." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Captura Instantânea Concluída" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Captura Instantânea Falhou" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Captura Instantânea em Progresso" msgid "Stack Events" msgstr "Eventos de Pilha" msgid "Stack ID" msgstr "ID da Pilha" msgid "Stack ID =" msgstr "ID da Pilha=" msgid "Stack Name" msgstr "Nome da Pilha" msgid "Stack Name =" msgstr "Nome da Pilha=" msgid "Stack Parameters" msgstr "Parâmetros da Pilha" msgid "Stack Preview" msgstr "Pré-visualização da Pilha" msgid "Stack Resource" msgstr "Recurso de Pilha" msgid "Stack Resource ID" msgstr "ID de Recurso da Pilha" msgid "Stack Resource Type" msgstr "Tipo de Recurso de Pilha" msgid "Stack Resources" msgstr "Recursos de Pilha" msgid "Stack creation started." msgstr "Criação da pilha iniciada." msgid "Stack creation timeout in minutes." msgstr "Tempo limite de criação da pilha em minutos." msgid "Stack update started." msgstr "Atualização de pilha iniciada." msgid "Stack updating timeout in minutes." msgstr "Timeout de atualização da pilha em minutos." msgid "Stacks" msgstr "Pilhas" msgid "Status" msgstr "Status" msgid "Status =" msgstr "Status =" msgid "Status Reason" msgstr "Razão do Status" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Suspensão Completa" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Suspensão Falhou" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Suspensão em Progresso" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Suspender Pilha" msgstr[1] "Suspender Pilhas" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Pilha Suspensa" msgstr[1] "Pilhas Suspensas" msgid "Template" msgstr "Modelo" msgid "Template Data" msgstr "Dados do Modelo" msgid "Template File" msgstr "Arquivo de Modelo" msgid "Template Functions" msgstr "Funções de Modelos" msgid "Template Generator" msgstr "Gerador de template" msgid "Template Source" msgstr "Origem do Modelo" msgid "Template URL" msgstr "URL de Modelo" msgid "Template Version" msgstr "Versão do Modelo" msgid "Template Versions" msgstr "Versões de Modelos" msgid "The raw contents of the environment file." msgstr "O conteúdo bruto do arquivo de ambiente." msgid "The raw contents of the template." msgstr "O conteúdo bruto do modelo." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "Houve um problema ao analisar o %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Isto é necessário para operações a serem executadas em todo o ciclo de vida " "da pilha" msgid "Time Since Event" msgstr "Tempo Desde o Evento" msgid "Timeout" msgstr "Tempo de espera" msgid "Topology" msgstr "Topologia" msgid "Type" msgstr "Tipo" msgid "Type =" msgstr "Tipo =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Não é possível buscar eventos para a pilha \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Impossível obter funções para o modelo versão \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Não é possível obter recursos para a pilha \"%s\"." msgid "Unable to retrieve metadata." msgstr "Não é possível recuperar metadados." msgid "Unable to retrieve resource type details." msgstr "Não é possível recuperar detalhes de tipo de recurso." msgid "Unable to retrieve resource." msgstr "Não é possível recuperar recurso." msgid "Unable to retrieve stack resource types." msgstr "Não é possível recuperar os tipos de recursos da pilha." msgid "Unable to retrieve stack template." msgstr "Não foi possível recuperar o modelo de pilha." msgid "Unable to retrieve stack." msgstr "Não é possível recuperar pilha." msgid "Unable to retrieve template functions." msgstr "Incapaz de recuperar as funções de template." msgid "Unable to retrieve template versions." msgstr "Incapaz de recuperar as versões de template." msgid "Update" msgstr "Atualizar" msgctxt "current status of stack" msgid "Update Complete" msgstr "Atualização Concluída" msgctxt "current status of stack" msgid "Update Failed" msgstr "A Atualização Falhou" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Atualização em Andamento" msgid "Update Stack" msgstr "Atualizar Pilha" msgid "Update Stack Parameters" msgstr "Atualizar Parâmetros de Pilha" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "Atualizar uma pilha com os valores fornecidos. Por favor, observe que " "quaisquer parâmetros criptografados, tais como senhas, serão restabelecidos " "ao padrão se você não alterá-los aqui." msgid "Updated" msgstr "Atualizado" msgid "Updating Timeout (minutes)" msgstr "Tempo Limite de Atualização (minutos)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "Utilize uma das opções de fonte de modelos disponíveis para especificar o " "modelo a ser usado na alteração dessa pilha." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "Use uma das opções de fonte de modelo disponíveis para especificar o modelo " "a ser utilizado na criação dessa pilha." msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "Use uma das opções de fonte de modelo disponíveis para especificar o modelo " "a ser utilizado na pré-visualização dessa pilha." msgid "Version" msgstr "Versão" msgid "You must specify a template via one of the available sources." msgstr "Você deve especificar um modelo via uma das origens disponíveis." msgid "environment" msgstr "ambiente" msgid "template" msgstr "modelo" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.505496 heat_dashboard-16.0.0/heat_dashboard/locale/pt_BR/LC_MESSAGES/djangojs.po0000664000175000017500000000632515257164523024511 0ustar00zuulzuul# Carlos Marques , 2016. #zanata # Fernando Pimenta , 2017. #zanata # Marcio , 2017. #zanata # Fernando Pimenta , 2018. #zanata # Rodrigo Loures , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-06-14 07:08+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-06-19 11:15+0000\n" "Last-Translator: Fernando Pimenta \n" "Language-Team: Portuguese (Brazil)\n" "Language: pt_BR\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "Cancel" msgstr "Cancelar" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Verificar Pilha" msgstr[1] "Verificar Pilhas" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Pilha Verificada: %s." msgstr[1] "Pilhas Verificadas: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Confirmar Verificação de Pilha" msgstr[1] "Confirmar Verificação de Pilhas" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Confirmar Exclusão de Pilha" msgstr[1] "Confirmar Exclusão de Pilhas" msgid "Create Stack" msgstr "Criar Pilha" msgid "Delete Resource" msgstr "Excluir Recursos" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Excluir Pilha" msgstr[1] "Excluir Pilhas" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Pilha Excluída: %s." msgstr[1] "Pilhas Excluídas: %s." msgid "Download" msgstr "Baixar" msgid "Heat Orchestration Template" msgstr "Modelo de Orquestração do Heat" msgid "OK" msgstr "OK" msgid "Save" msgstr "Salvar" msgid "Saved Drafts" msgstr "Rascunhos Armazenados" msgid "Show More Properties" msgstr "Mostrar mais Propriedades" #, python-format msgid "Stack %s was successfully created." msgstr "Pilha %s foi criada com sucesso." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Não é possível verificar Pilha: %s." msgstr[1] "Não é possível verificar Pilhas: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Não é possível excluir Pilha: %s." msgstr[1] "Não é possível excluir Pilhas: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "Você não tem permissão para verificar as pilhas: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "Você não tem permissão para excluir pilhas: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "Você selecionou \"%s\"." msgstr[1] "Você selecionou \"%s\"." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "Você selecionou \"%s\". Pilha excluída não é recuperável." msgstr[1] "Você selecionou \"%s\". Pilhas excluídas não são recuperáveis." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.264263 heat_dashboard-16.0.0/heat_dashboard/locale/ru/0000775000175000017500000000000015257164606020201 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3232615 heat_dashboard-16.0.0/heat_dashboard/locale/ru/LC_MESSAGES/0000775000175000017500000000000015257164606021766 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6624951 heat_dashboard-16.0.0/heat_dashboard/locale/ru/LC_MESSAGES/django.po0000664000175000017500000004515315257164523023576 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Aleksey Alekseenko <9118250541@mail.ru>, 2016. #zanata # Alexander , 2016. #zanata # Andreas Jaeger , 2016. #zanata # Artem , 2016. #zanata # Daisy , 2016. #zanata # Irina Kochetova , 2016. #zanata # Maxim Bozhenko , 2016. #zanata # Nikita Burtsev , 2016. #zanata # Artem , 2017. #zanata # Fedor Tarasenko , 2017. #zanata # Ilya Alekseyev , 2017. #zanata # Roman Gorshunov , 2019. #zanata # Roman Gorshunov , 2021. #zanata # Dmitriy Chubinidze , 2025. #zanata # Ivan Anfimov , 2025. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2025-08-30 11:21+0000\n" "Last-Translator: Ivan Anfimov \n" "Language-Team: Russian\n" "Language: ru\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "ЛокальнаÑ� Ñ�реда длÑ� загрузки." msgid "A local template to upload." msgstr "Локальный шаблон длÑ� загрузки." msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "" "Шаблон иÑ�пользуетÑ�Ñ� длÑ� автоматизации развёртываниÑ� инфраÑ�труктуры, Ñ�ервиÑ�ов " "и приложений." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "ПринÑ�тие завершено" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Ошибка принÑ�тиÑ�" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "ВыполнÑ�етÑ�Ñ� принÑ�тие" msgid "An external (HTTP) URL to load the template from." msgstr "Внешний (HTTP) URL длÑ� загрузки образа." msgid "Attributes" msgstr "Ð�ттрибуты" msgid "Case sensitive" msgstr "ЧувÑ�твительно к региÑ�тру" msgid "Case-sensitive" msgstr "ЧувÑ�твительно к региÑ�тру" msgid "Change Stack Template" msgstr "Изменить шаблон Ñ�тека" msgid "Change Template" msgstr "Сменить шаблон" msgctxt "current status of stack" msgid "Check Complete" msgstr "Проверка завершена" msgctxt "current status of stack" msgid "Check Failed" msgstr "Ошибка проверки" msgctxt "current status of stack" msgid "Check In Progress" msgstr "ВыполнÑ�етÑ�Ñ� проверка" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Проверить Ñ�тек" msgstr[1] "Проверить Ñ�теки" msgstr[2] "Проверить Ñ�теки" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Стек проверен" msgstr[1] "Стеки проверены" msgstr[2] "Стеки проверены" msgid "Clear Canvas" msgstr "ОчиÑ�тить поле" msgid "Close" msgstr "Закрыть" msgctxt "current status of stack" msgid "Create Complete" msgstr "Создание завершено" msgctxt "current status of stack" msgid "Create Failed" msgstr "Ошибка Ñ�озданиÑ�" msgctxt "current status of stack" msgid "Create In Progress" msgstr "ВыполнÑ�етÑ�Ñ� Ñ�оздание" msgid "Create Stack" msgstr "Создать Ñ�тек" msgid "Create a new stack with the provided values." msgstr "Создать новый Ñ�тек Ñ� предоÑ�тавленными параметрами." msgid "Created" msgstr "Создан" msgid "Creation Timeout (minutes)" msgstr "Таймаут Ñ�озданиÑ� (мин.)" msgid "Date Updated" msgstr "Дата обновлениÑ�" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Удаление завершено" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Удаление не удалоÑ�ÑŒ" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "ВыполнÑ�етÑ�Ñ� удаление" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Удалить Ñ�тек" msgstr[1] "Удалить Ñ�теки" msgstr[2] "Удалить Ñ�теки" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Стек удален" msgstr[1] "Стеки удалены" msgstr[2] "Стеки удалены" msgid "Description" msgstr "ОпиÑ�ание" msgid "Description:" msgstr "ОпиÑ�ание:" msgid "Direct Input" msgstr "ПрÑ�мой ввод" msgid "Disabled" msgstr "Выключен" msgid "Edit Template" msgstr "Изменить шаблон" msgid "Enable rollback on create/update failure." msgstr "Включить откат при Ñ�бое Ñ�озданиÑ�/обновлениÑ�." msgid "Enabled" msgstr "Включен" msgid "Environment Data" msgstr "Данные Ñ�реды" msgid "Environment File" msgstr "Файл Ñ�реды" msgid "Environment Source" msgstr "ИÑ�точник Ñ�реды" msgid "Events" msgstr "СобытиÑ�" msgid "Export Draft" msgstr "ЭкÑ�портировать черновик" msgid "File" msgstr "Файл" msgid "Function" msgstr "ФункциÑ�" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "Импортировать черновик" msgctxt "current status of stack" msgid "Init Complete" msgstr "ИнициализациÑ� завершена" msgctxt "current status of stack" msgid "Init Failed" msgstr "Ошибка инициализации" msgctxt "current status of stack" msgid "Init In Progress" msgstr "ВыполнÑ�етÑ�Ñ� инициализациÑ�" msgid "Last Updated" msgstr "ПоÑ�леднее обновление" msgid "Launch" msgstr "ЗапуÑ�тить" msgid "Launch Parameters" msgstr "Параметры запуÑ�ка" msgid "Launch Stack" msgstr "ЗапуÑ�тить Ñ�тек" msgid "Links" msgstr "СÑ�ылки" msgid "Load Draft" msgstr "Загрузить черновик" msgid "Manage Drafts" msgstr "Управление черновиками" msgid "Minutes" msgstr "Мин." msgid "Name" msgstr "ИмÑ�" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "ИмÑ� должно начинатьÑ�Ñ� Ñ� буквы и может Ñ�одержать только буквы, цифры, Ñ�имволы " "подчеркиваниÑ�, точки и дефиÑ�Ñ‹." msgid "Name of the stack to create." msgstr "ИмÑ� Ñ�оздаваемого Ñ�тека." msgid "Next" msgstr "Далее" msgid "No images available" msgstr "Ð�ет доÑ�тупных образов" msgid "Orchestration" msgstr "ОркеÑ�трациÑ�" msgid "Outputs" msgstr "Выводы" msgid "Overview" msgstr "Обзор" msgid "Parameters" msgstr "Параметры" #, python-format msgid "Password for user \"%s\"" msgstr "Пароль длÑ� пользователÑ� \"%s\"" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "" "ПожалуйÑ�та, перетащите мышкой иконки реÑ�урÑ�ов в центр полÑ�, чтобы задать " "реÑ�урÑ�Ñ‹ длÑ� шаблона." #, python-format msgid "Please specify a %s using only one source method." msgstr "ПожалуйÑ�та, укажите %s, иÑ�пользуÑ� только один метод иÑ�точника." msgid "Preview" msgstr "Предварительный проÑ�мотр" msgid "Preview Stack" msgstr "Предварительный проÑ�мотр Ñ�тека" msgid "Preview Stack Details" msgstr "Предварительный проÑ�мотр подробноÑ�тей Ñ�тека" msgid "Preview Stack Parameters" msgstr "Предварительный проÑ�мотр параметров Ñ�тека" msgid "Preview Template" msgstr "Предварительный проÑ�мотр шаблона" msgid "Preview a new stack with the provided values." msgstr "Предварительный проÑ�мотр Ñ�тека Ñ� предоÑ�тавленными значениÑ�ми." msgid "Properties" msgstr "СвойÑ�тва" msgid "Referenced Files" msgstr "СвÑ�занные файлы" msgid "Resource" msgstr "РеÑ�урÑ�" msgid "Resource ID" msgstr "ID реÑ�урÑ�а" msgid "Resource Metadata" msgstr "Метаданные реÑ�урÑ�а" msgid "Resource Types" msgstr "Типы реÑ�урÑ�а" msgid "Resources" msgstr "РеÑ�урÑ�Ñ‹" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Возобновление завершено" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Ошибка возобновлениÑ�" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "ВыполнÑ�етÑ�Ñ� возобновление" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Возобновить Ñ�тек" msgstr[1] "Возобновить Ñ�теки" msgstr[2] "Возобновить Ñ�теки" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Стек возобновлен" msgstr[1] "Стеки возобновлены" msgstr[2] "Стеки возобновлены" msgid "Rollback" msgstr "Откат" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Откат изменений завершен" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Откат изменений не удалÑ�Ñ�" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "ВыполнÑ�етÑ�Ñ� откат изменений" msgid "Rollback On Failure" msgstr "ОткатитьÑ�Ñ� при Ñ�бое" msgid "Save Draft" msgstr "Сохранить черновик" msgid "Select Image" msgstr "Выберите образ" msgid "Select Template" msgstr "Выберите шаблон" msgid "Select a new template to preview a stack." msgstr "Выберите новый шаблон длÑ� предварительного проÑ�мотра Ñ�тека." msgid "Select a new template to re-launch a stack." msgstr "Выберите новый шаблон длÑ� повторного запуÑ�ка Ñ�тека." msgid "Select a template to launch a stack." msgstr "Выберите шаблон длÑ� запуÑ�ка Ñ�тека." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Снимок Ñ�оздан уÑ�пешно" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Ошибка Ñ�озданиÑ� Ñ�нимка " msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "СоздаетÑ�Ñ� Ñ�нимок" msgid "Stack Events" msgstr "СобытиÑ� Ñ�тека" msgid "Stack ID" msgstr "ID Ñ�тека" msgid "Stack ID =" msgstr "ID Ñ�тека =" msgid "Stack Name" msgstr "ИмÑ� Ñ�тека" msgid "Stack Name =" msgstr "ИмÑ� Ñ�тека =" msgid "Stack Parameters" msgstr "Параметры Ñ�тека" msgid "Stack Preview" msgstr "Предварительный проÑ�мотр Ñ�тека" msgid "Stack Resource" msgstr "РеÑ�урÑ� Ñ�тека" msgid "Stack Resource ID" msgstr "ID реÑ�урÑ�а Ñ�тека" msgid "Stack Resource Type" msgstr "Тип реÑ�урÑ�а Ñ�тека" msgid "Stack Resources" msgstr "РеÑ�урÑ�Ñ‹ Ñ�тека" msgid "Stack creation started." msgstr "Ð�ачалоÑ�ÑŒ Ñ�оздание Ñ�тека." msgid "Stack creation timeout in minutes." msgstr "Таймаут Ñ�озданиÑ� Ñ�тека в минутах." msgid "Stack update started." msgstr "Ð�ачалоÑ�ÑŒ обновление Ñ�тека." msgid "Stack updating timeout in minutes." msgstr "Таймаут обновлениÑ� Ñ�тека в минутах." msgid "Stacks" msgstr "Стеки" msgid "Status" msgstr "СтатуÑ�" msgid "Status =" msgstr "СтатуÑ� =" msgid "Status Reason" msgstr "Причина Ñ�татуÑ�а" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "ОÑ�тановка завершена" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Ошибка оÑ�тановки" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "ВыполнÑ�етÑ�Ñ� оÑ�тановка" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "ОÑ�тановить Ñ�тек" msgstr[1] "ОÑ�тановить Ñ�теки" msgstr[2] "ОÑ�тановить Ñ�теки" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Стек оÑ�тановлен" msgstr[1] "Стеки оÑ�тановлены" msgstr[2] "Стеки оÑ�тановлены" msgid "Template" msgstr "Шаблон" msgid "Template Data" msgstr "Данные шаблона" msgid "Template File" msgstr "Файл шаблона" msgid "Template Functions" msgstr "Функции шаблона" msgid "Template Generator" msgstr "Генератор шаблона" msgid "Template Source" msgstr "ИÑ�точник шаблона" msgid "Template URL" msgstr "URL шаблона" msgid "Template Version" msgstr "ВерÑ�иÑ� шаблона" msgid "Template Versions" msgstr "ВерÑ�ии шаблона" msgid "The raw contents of the environment file." msgstr "Содержимое файла Ñ�реды (raw). " msgid "The raw contents of the template." msgstr "Содержимое шаблона (raw)." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "Возникла проблема при парÑ�инге %(prefix)s: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "" "Это необходимо длÑ� выполнениÑ� операций на протÑ�жении вÑ�его жизненного цикла " "Ñ�тека" msgid "Time Since Event" msgstr "ВремÑ� Ñ� момента Ñ�обытиÑ�" msgid "Timeout" msgstr "Таймаут" msgid "Topology" msgstr "ТопологиÑ�" msgid "Type" msgstr "Тип" msgid "Type =" msgstr "Тип =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Ð�е удалоÑ�ÑŒ получить Ñ�обытиÑ� длÑ� Ñ�тека \"%s\"." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "Ð�е удалоÑ�ÑŒ получить функции длÑ� шаблона верÑ�ии \"%s\"." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Ð�е удалоÑ�ÑŒ получить реÑ�урÑ�Ñ‹ длÑ� Ñ�тека \"%s\"." msgid "Unable to retrieve images" msgstr "Ð�е удалоÑ�ÑŒ получить образы" msgid "Unable to retrieve metadata." msgstr "Ð�е удалоÑ�ÑŒ получить метаданные." msgid "Unable to retrieve resource type details." msgstr "Ð�е удалоÑ�ÑŒ получить подробноÑ�ти типа реÑ�урÑ�а." msgid "Unable to retrieve resource." msgstr "Ð�е удалоÑ�ÑŒ получить реÑ�урÑ�." msgid "Unable to retrieve stack resource types." msgstr "Ð�е удалоÑ�ÑŒ получить типы реÑ�урÑ�а Ñ�тека." msgid "Unable to retrieve stack template." msgstr "Ð�е удалоÑ�ÑŒ получить шаблон Ñ�тека." msgid "Unable to retrieve stack." msgstr "Ð�е удалоÑ�ÑŒ получить Ñ�тек." msgid "Unable to retrieve stacks list." msgstr "Ð�е удалоÑ�ÑŒ получить Ñ�пиÑ�ок Ñ�теков." msgid "Unable to retrieve template functions." msgstr "Ð�е удалоÑ�ÑŒ получить функции шаблона." msgid "Unable to retrieve template versions." msgstr "Ð�е удалоÑ�ÑŒ получить верÑ�ии шаблона." msgid "Update" msgstr "Обновить" msgctxt "current status of stack" msgid "Update Complete" msgstr "Изменение завершено" msgctxt "current status of stack" msgid "Update Failed" msgstr "Изменение не удалоÑ�ÑŒ" msgctxt "current status of stack" msgid "Update In Progress" msgstr "ВыполнÑ�етÑ�Ñ� изменение" msgid "Update Stack" msgstr "Обновить Ñ�тек" msgid "Update Stack Parameters" msgstr "Обновить параметры Ñ�тека" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "Обновите Ñ�тек, иÑ�пользуÑ� предоÑ�тавленные значениÑ�. Обратите внимание, что " "любые зашифрованные параметры, такие как пароли, будут Ñ�брошены до значений " "по умолчанию, еÑ�ли вы не измените их здеÑ�ÑŒ." msgid "Updated" msgstr "Обновлен" msgid "Updating Timeout (minutes)" msgstr "Обновление таймаута (мин.)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "" "ИÑ�пользуйте одну из доÑ�тупных опций иÑ�точника шаблона длÑ� выбора шаблона, " "который будет иÑ�пользоватьÑ�Ñ� при изменении Ñ�того Ñ�тека." msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "ИÑ�пользуйте одну из доÑ�тупных опций иÑ�точника шаблона длÑ� выбора шаблона, " "который будет иÑ�пользоватьÑ�Ñ� при Ñ�оздании Ñ�того Ñ�тека." msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "" "ИÑ�пользуйте одну из доÑ�тупных опций иÑ�точника шаблона длÑ� выбора шаблона, " "который будет иÑ�пользоватьÑ�Ñ� в предварительном проÑ�мотре Ñ�тека." msgid "Version" msgstr "ВерÑ�иÑ�" msgid "You must specify a template via one of the available sources." msgstr "Вам необходимо указать шаблон через один из доÑ�тупных иÑ�точников." msgid "environment" msgstr "Ñ�реда" msgid "template" msgstr "шаблон" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6634953 heat_dashboard-16.0.0/heat_dashboard/locale/ru/LC_MESSAGES/djangojs.po0000664000175000017500000001050115257164523024120 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Maxim Bozhenko , 2016. #zanata # Roman Gorshunov , 2019. #zanata # Dmitriy Chubinidze , 2025. #zanata # Ivan Anfimov , 2025. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-15 09:13+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2025-08-26 06:58+0000\n" "Last-Translator: Ivan Anfimov \n" "Language-Team: Russian\n" "Language: ru\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" msgid "Cancel" msgstr "Отмена" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Проверить Ñ�тек" msgstr[1] "Проверить Ñ�теки" msgstr[2] "Проверить Ñ�теки" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "Проверенный Ñ�тек: %s." msgstr[1] "Проверенные Ñ�теки: %s." msgstr[2] "Проверенные Ñ�теки: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "Подтвердите проверку Ñ�тека" msgstr[1] "Подтвердите проверку Ñ�теков" msgstr[2] "Подтвердите проверку Ñ�теков" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "Подтвердить удаление Ñ�тека" msgstr[1] "Подтвердить удаление Ñ�теков" msgstr[2] "Подтвердить удаление Ñ�теков" msgid "Create Stack" msgstr "Создать Ñ�тек" msgid "Delete Resource" msgstr "Удалить реÑ�урÑ�" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Удалить Ñ�тек" msgstr[1] "Удалить Ñ�теки" msgstr[2] "Удалить Ñ�теки" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "Стек удален: %s." msgstr[1] "Стеки удалены: %s." msgstr[2] "Стеки удалены: %s." msgid "Download" msgstr "Скачать" msgid "Heat Orchestration Template" msgstr "Шаблон оркеÑ�трации Heat" msgid "OK" msgstr "ОК" msgid "Save" msgstr "Сохранить" msgid "Saved Drafts" msgstr "Сохраненные черновики" msgid "Show More Properties" msgstr "Показать больше Ñ�войÑ�тв" #, python-format msgid "Stack %s was successfully created." msgstr "Стек %s уÑ�пешно Ñ�оздан." #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "Ð�е удалоÑ�ÑŒ проверить Ñ�тек: %s." msgstr[1] "Ð�е удалоÑ�ÑŒ проверить Ñ�теки: %s." msgstr[2] "Ð�е удалоÑ�ÑŒ проверить Ñ�теки: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "Ð�е удалоÑ�ÑŒ удалить Ñ�тек: %s." msgstr[1] "Ð�е удалоÑ�ÑŒ удалить Ñ�теки: %s." msgstr[2] "Ð�е удалоÑ�ÑŒ удалить Ñ�теки: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "Вам не разрешено проверÑ�ть Ñ�теки: %s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "Вам не разрешено удалÑ�ть Ñ�теки: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "Ð’Ñ‹ выбрали \"%s\"." msgstr[1] "Ð’Ñ‹ выбрали \"%s\"." msgstr[2] "Ð’Ñ‹ выбрали \"%s\"." #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "Ð’Ñ‹ выбрали \"%s\". Удаленный Ñ�тек не подлежит воÑ�Ñ�тановлению." msgstr[1] "Ð’Ñ‹ выбрали \"%s\". Удаленные Ñ�теки не подлежат воÑ�Ñ�тановлению." msgstr[2] "Ð’Ñ‹ выбрали \"%s\". Удаленные Ñ�теки не подлежат воÑ�Ñ�тановлению." ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2652628 heat_dashboard-16.0.0/heat_dashboard/locale/tr_TR/0000775000175000017500000000000015257164606020605 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3232615 heat_dashboard-16.0.0/heat_dashboard/locale/tr_TR/LC_MESSAGES/0000775000175000017500000000000015257164606022372 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5064962 heat_dashboard-16.0.0/heat_dashboard/locale/tr_TR/LC_MESSAGES/django.po0000664000175000017500000002463715257164523024206 0ustar00zuulzuul# OpenStack Infra , 2015. #zanata # iÅŸbaran akçayır , 2017. #zanata # yasemin demiral , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-10-23 08:56+0000\n" "Last-Translator: Copied by Zanata \n" "Language-Team: Turkish (Turkey)\n" "Language: tr_TR\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n>1)\n" msgid "A local environment to upload." msgstr "Yüklenecek yerel çevre." msgid "A local template to upload." msgstr "Yüklenecek yerel bir ÅŸablon." msgctxt "current status of stack" msgid "Adopt Complete" msgstr "Uyum SaÄŸlama Tamamlandı" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "Uyum SaÄŸlama BaÅŸarısız" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "Uyum SaÄŸlama Sürüyor" msgid "An external (HTTP) URL to load the template from." msgstr "Åžablonun yükleneceÄŸi harici (HTTP) URL." msgid "Case sensitive" msgstr "Büyük küçük harf duyarlı" msgid "Case-sensitive" msgstr "Büyük küçük harf duyarlı" msgid "Change Stack Template" msgstr "Yığın Åžablonunu DeÄŸiÅŸtir" msgid "Change Template" msgstr "Åžablon DeÄŸiÅŸtir" msgctxt "current status of stack" msgid "Check Complete" msgstr "Kontrol Tamamlandı" msgctxt "current status of stack" msgid "Check Failed" msgstr "Kontrol BaÅŸarısız" msgctxt "current status of stack" msgid "Check In Progress" msgstr "Kontrol Sürüyor" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "Yığını Kontrol Et" msgstr[1] "Yığınları Kontrol Et" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "Yığın Kontrol Edildi" msgstr[1] "Yığınlar Kontrol Edildi" msgctxt "current status of stack" msgid "Create Complete" msgstr "OluÅŸturma Tamamlandı" msgctxt "current status of stack" msgid "Create Failed" msgstr "OluÅŸturma BaÅŸarısız" msgctxt "current status of stack" msgid "Create In Progress" msgstr "OluÅŸturma Sürüyor" msgid "Create Stack" msgstr "Yığın OluÅŸtur" msgid "Created" msgstr "OluÅŸturuldu" msgid "Creation Timeout (minutes)" msgstr "OluÅŸturma Zaman Aşımı (dakika)" msgid "Date Updated" msgstr "Tarih Güncellendi" msgctxt "current status of stack" msgid "Delete Complete" msgstr "Silme Tamamlandı" msgctxt "current status of stack" msgid "Delete Failed" msgstr "Silme BaÅŸarısız" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "Silme Sürüyor" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "Yığını Sil" msgstr[1] "Yığınları Sil" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "Yığın Silindi" msgstr[1] "Yığınlar Silindi" msgid "Description" msgstr "Açıklama" msgid "Direct Input" msgstr "DoÄŸrudan Girdi" msgid "Edit Template" msgstr "Åžamblonu Düzenle" msgid "Enable rollback on create/update failure." msgstr "OluÅŸtur/güncelle baÅŸarısızlığında geri dönmeyi etkinleÅŸtir." msgid "Environment Data" msgstr "Çevre Verisi" msgid "Environment File" msgstr "Çevre Dosyası" msgid "Environment Source" msgstr "Çevre Kaynağı" msgid "Events" msgstr "Etkinlikler" msgid "File" msgstr "Dosya" msgid "Function" msgstr "Fonksiyon" msgctxt "current status of stack" msgid "Init Complete" msgstr "Init Tamamlandı" msgctxt "current status of stack" msgid "Init Failed" msgstr "Init BaÅŸarısız" msgctxt "current status of stack" msgid "Init In Progress" msgstr "Init Sürüyor" msgid "Launch" msgstr "BaÅŸlat" msgid "Launch Stack" msgstr "Yığını BaÅŸlat" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "" "İsim bir harfle baÅŸlamalı ve yalnızca harf, numaralar, alt çizgiler, nokta " "ve tireler içermelidir." msgid "Name of the stack to create." msgstr "OluÅŸturulacak yığının ismi." msgid "Next" msgstr "Sonraki" msgid "Orchestration" msgstr "Orkestrasyon" msgid "Overview" msgstr "Genel görünüm" #, python-format msgid "Password for user \"%s\"" msgstr "\"%s\" kullanıcısı için parola" #, python-format msgid "Please specify a %s using only one source method." msgstr "Lütfen yalnızca bir kaynak metodu kullanarak %s belirtin." msgid "Preview" msgstr "Önizleme" msgid "Preview Stack" msgstr "Yığını Ön İzle" msgid "Preview Stack Details" msgstr "Yığın Detaylarını Önizle" msgid "Preview Stack Parameters" msgstr "Yığın Parametrelerini Önizleyin" msgid "Preview Template" msgstr "Åžablon Önizlemesi" msgid "Resource" msgstr "Kaynak" msgid "Resource Types" msgstr "Kaynak Türleri" msgid "Resources" msgstr "Kaynaklar" msgctxt "current status of stack" msgid "Resume Complete" msgstr "Sürdürme Tamamlandı" msgctxt "current status of stack" msgid "Resume Failed" msgstr "Sürdürme BaÅŸarısız" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "Sürdürme Sürüyor" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "Yığını Sürdür" msgstr[1] "Yığınları Sürdür" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "Yığın Sürdürüldü" msgstr[1] "Yığınlar Sürdürüldü" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "Geri Alma Tamamlandı" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "Geri Alma BaÅŸarısız" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "Geri Alma Sürüyor" msgid "Rollback On Failure" msgstr "BaÅŸarısızlık Durumunda Geri Dön" msgid "Select Template" msgstr "Åžablon Seç" msgid "Select a new template to preview a stack." msgstr "Bir yığını ön izlemek için yeni bir ÅŸablon seçin." msgid "Select a new template to re-launch a stack." msgstr "Yığını tekrar baÅŸlatmak için yeni bir ÅŸablon seçin." msgid "Select a template to launch a stack." msgstr "Bir yığın baÅŸlatmak için ÅŸablon seçin." msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "Anlık Görüntü Tamamlandı" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "Anlık Görüntü BaÅŸarısız" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "Anlık Görüntü Sürüyor" msgid "Stack Events" msgstr "Yığın Olayları" msgid "Stack ID" msgstr "Yığın ID'si" msgid "Stack ID =" msgstr "Yığın ID'si =" msgid "Stack Name" msgstr "Yığın İsmi" msgid "Stack Name =" msgstr "Yığın İsmi =" msgid "Stack Resource" msgstr "Yığın Kaynağı" msgid "Stack Resource Type" msgstr "Yığın Kaynak Türü" msgid "Stack Resources" msgstr "Yığın Kaynakları" msgid "Stack creation started." msgstr "Yığın oluÅŸturma baÅŸladı." msgid "Stack creation timeout in minutes." msgstr "Dakika olarak yığın oluÅŸturma zaman aşımı." msgid "Stack update started." msgstr "Yığın güncellemesi baÅŸlatıldı." msgid "Stacks" msgstr "Yığınlar" msgid "Status" msgstr "Durum" msgid "Status =" msgstr "Durum =" msgid "Status Reason" msgstr "Durum Sebebi" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "Askıya Alma Tamamlandı" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "Askıya Alma BaÅŸarısız" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "Askıya Alma Sürüyor" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "Yığını Askıya Al" msgstr[1] "Yığınları Askıya Al" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "Yığın Askıya Alındı" msgstr[1] "Yığınlar Askıya Alındı" msgid "Template" msgstr "Åžablon" msgid "Template Data" msgstr "Åžablon Verisi" msgid "Template File" msgstr "Åžablon Dosyası" msgid "Template Functions" msgstr "Åžablon Fonksiyonları" msgid "Template Source" msgstr "Åžablon Kaynağı" msgid "Template URL" msgstr "Åžablon URL'si" msgid "Template Versions" msgstr "Åžablon Sürümleri" msgid "The raw contents of the environment file." msgstr "Çevre dosyasının ham içerikleri." msgid "The raw contents of the template." msgstr "Åžablonun ham içerikleri." #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "%(prefix)s ayrıştırmasında hata: %(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "Yığının ömrü sürecinde yürütülecek iÅŸlemler için gereklidir" msgid "Time Since Event" msgstr "Olaydan Beri Geçen Zaman" msgid "Topology" msgstr "Topoloji" msgid "Type" msgstr "Tür" msgid "Type =" msgstr "Tür =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "Yığın \"%s\" için olaylar alınamıyor." #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "\"%s\" sürümlü ÅŸablon için fonksiyonlar alınamıyor." #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "Yığın \"%s\" için kaynaklar alınamıyor." msgid "Unable to retrieve metadata." msgstr "Metaveri alınamıyor." msgid "Unable to retrieve resource type details." msgstr "Kaynak türü ayrıntıları alınamadı." msgid "Unable to retrieve resource." msgstr "Kaynak alınamıyor." msgid "Unable to retrieve stack resource types." msgstr "Yığın kaynak türleri alınamadı." msgid "Unable to retrieve stack template." msgstr "Yığın ÅŸablonu alınamadı." msgid "Unable to retrieve stack." msgstr "Yığın alınamıyor." msgid "Unable to retrieve template functions." msgstr "Åžablon fonksiyonları alınamıyor." msgid "Unable to retrieve template versions." msgstr "Åžablon sürümleri alınamıyor." msgid "Update" msgstr "Güncelle" msgctxt "current status of stack" msgid "Update Complete" msgstr "Güncelleme Tamamlandı" msgctxt "current status of stack" msgid "Update Failed" msgstr "Güncelleme BaÅŸarısız" msgctxt "current status of stack" msgid "Update In Progress" msgstr "Güncelleme Sürüyor" msgid "Update Stack" msgstr "Yığını Güncelle" msgid "Update Stack Parameters" msgstr "Yığın Parametrelerini Güncelle" msgid "Updated" msgstr "Güncellendi" msgid "Version" msgstr "Sürüm" msgid "You must specify a template via one of the available sources." msgstr "" "Kullanılabilir kaynaklardan biri aracılığıyla bir ÅŸablon belirtmelisiniz" msgid "environment" msgstr "çevre" msgid "template" msgstr "ÅŸablon" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2652628 heat_dashboard-16.0.0/heat_dashboard/locale/zh_Hans/0000775000175000017500000000000015257164606021145 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3242614 heat_dashboard-16.0.0/heat_dashboard/locale/zh_Hans/LC_MESSAGES/0000775000175000017500000000000015257164606022732 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.507496 heat_dashboard-16.0.0/heat_dashboard/locale/zh_Hans/LC_MESSAGES/django.po0000664000175000017500000003153715257164523024543 0ustar00zuulzuul# BillXiang , 2016. #zanata # Linda , 2016. #zanata # Tony , 2016. #zanata # howard lee , 2016. #zanata # liuyanfu , 2016. #zanata # maoshuai , 2016. #zanata # sunanchen , 2016. #zanata # zzxwill , 2016. #zanata # Bin , 2017. #zanata # TigerFang , 2017. #zanata # Tony , 2017. #zanata # bkb , 2017. #zanata # zhonghuali , 2017. #zanata # Tony , 2018. #zanata # Tony , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2025-08-30 03:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-03-20 01:38+0000\n" "Last-Translator: Tony \n" "Language-Team: Chinese (China)\n" "Language: zh_CN\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" #, python-format msgid "%(stack_status_title)s: %(stack_status_reason)s" msgstr "%(stack_status_title)s: %(stack_status_reason)s" msgid "A local environment to upload." msgstr "上传一个本地环境文件" msgid "A local template to upload." msgstr "è¦�上传的本地模æ�¿ã€‚" msgid "" "A template is used to automate the deployment of infrastructure, services, " "and applications." msgstr "一个用于对基础架构ã€�æœ�务和应用部署进行自动化的模æ�¿ã€‚" msgctxt "current status of stack" msgid "Adopt Complete" msgstr "é…�置完æˆ�" msgctxt "current status of stack" msgid "Adopt Failed" msgstr "é…�置失败" msgctxt "current status of stack" msgid "Adopt In Progress" msgstr "正在å�¯ç”¨é…�ç½®" msgid "An external (HTTP) URL to load the template from." msgstr "è¦�通过其加载模æ�¿çš„外部 (HTTP) URL。" msgid "Attributes" msgstr "属性" msgid "Case sensitive" msgstr "区分大å°�写" msgid "Case-sensitive" msgstr "区分大å°�写" msgid "Change Stack Template" msgstr "改å�˜å †æ ˆæ¨¡æ�¿" msgid "Change Template" msgstr "更改模æ�¿" msgctxt "current status of stack" msgid "Check Complete" msgstr "检查完æˆ�" msgctxt "current status of stack" msgid "Check Failed" msgstr "检查失败" msgctxt "current status of stack" msgid "Check In Progress" msgstr "正在检查" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "检查栈" msgid "Checked Stack" msgid_plural "Checked Stacks" msgstr[0] "å·²ç»�检查过的堆栈" msgid "Clear Canvas" msgstr "清除画æ�¿" msgid "Close" msgstr "关闭" msgctxt "current status of stack" msgid "Create Complete" msgstr "创建完æˆ�" msgctxt "current status of stack" msgid "Create Failed" msgstr "创建失败" msgctxt "current status of stack" msgid "Create In Progress" msgstr "正在创建" msgid "Create Stack" msgstr "创建堆栈" msgid "Create a new stack with the provided values." msgstr "使用æ��供的值创建一个新堆栈。" msgid "Created" msgstr "已创建" msgid "Creation Timeout (minutes)" msgstr "创建超时(分钟)" msgid "Date Updated" msgstr "日期已更新" msgctxt "current status of stack" msgid "Delete Complete" msgstr "删除完æˆ�" msgctxt "current status of stack" msgid "Delete Failed" msgstr "删除失败" msgctxt "current status of stack" msgid "Delete In Progress" msgstr "正在删除" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "删除堆栈" msgid "Deleted Stack" msgid_plural "Deleted Stacks" msgstr[0] "已删除堆栈" msgid "Description" msgstr "æ��è¿°" msgid "Description:" msgstr "æ��述:" msgid "Direct Input" msgstr "直接输入" msgid "Disabled" msgstr "å·²ç¦�用" msgid "Edit Template" msgstr "编辑模æ�¿" msgid "Enable rollback on create/update failure." msgstr "在创建/更新失败时å…�许回滚。" msgid "Enabled" msgstr "å·²å�¯ç”¨" msgid "Environment Data" msgstr "环境数æ�®" msgid "Environment File" msgstr "环境文件" msgid "Environment Source" msgstr "环境æº�" msgid "Events" msgstr "事件" msgid "Export Draft" msgstr "导出è�‰ç¨¿" msgid "File" msgstr "文件" msgid "Function" msgstr "功能" msgid "ID" msgstr "ID" msgid "Import Draft" msgstr "导入è�‰ç¨¿" msgctxt "current status of stack" msgid "Init Complete" msgstr "åˆ�始化完æˆ�" msgctxt "current status of stack" msgid "Init Failed" msgstr "åˆ�始化失败" msgctxt "current status of stack" msgid "Init In Progress" msgstr "正在åˆ�始化" msgid "Last Updated" msgstr "最近更新" msgid "Launch" msgstr "å�¯åЍ" msgid "Launch Parameters" msgstr "å�¯åЍå�‚æ•°" msgid "Launch Stack" msgstr "创建堆栈" msgid "Links" msgstr "链接" msgid "Load Draft" msgstr "加载è�‰ç¨¿" msgid "Manage Drafts" msgstr "管ç�†è�‰ç¨¿" msgid "Minutes" msgstr "分钟" msgid "Name" msgstr "å��ç§°" msgid "" "Name must start with a letter and may only contain letters, numbers, " "underscores, periods and hyphens." msgstr "å��字必须以字æ¯�开始,并且å�ªèƒ½åŒ…å�«å­—æ¯�ã€�æ•°å­—ã€�下划线ã€�å�¥ç‚¹å’Œè¿žå­—符。" msgid "Name of the stack to create." msgstr "待创建堆栈的å��ç§°" msgid "Next" msgstr "下一项" msgid "Orchestration" msgstr "编排" msgid "Outputs" msgstr "输出" msgid "Overview" msgstr "概览" msgid "Parameters" msgstr "å�‚æ•°" #, python-format msgid "Password for user \"%s\"" msgstr "用户 “%sâ€� 的密ç �" msgid "" "Please drag and drop resource icons into center canvas to define resource " "for template." msgstr "请把资æº�图标拖到中部的画æ�¿ä¸­æ�¥å®šä¹‰æ¨¡æ�¿ã€‚" #, python-format msgid "Please specify a %s using only one source method." msgstr "请指定 %s(å�ªç”¨ä¸€ç§�æº�方法)。" msgid "Preview" msgstr "预览" msgid "Preview Stack" msgstr "堆栈预览" msgid "Preview Stack Details" msgstr "堆栈详情预览" msgid "Preview Stack Parameters" msgstr "预览堆栈å�‚æ•°" msgid "Preview Template" msgstr "预览模æ�¿" msgid "Preview a new stack with the provided values." msgstr "预览带有所æ��供值的新堆栈。" msgid "Properties" msgstr "属性" msgid "Referenced Files" msgstr "å�‚考的文件" msgid "Resource" msgstr "资æº�" msgid "Resource ID" msgstr "资æº� ID" msgid "Resource Metadata" msgstr "资æº�元数æ�®" msgid "Resource Types" msgstr "资æº�类型" msgid "Resources" msgstr "资æº�" msgctxt "current status of stack" msgid "Resume Complete" msgstr "æ�¢å¤�完æˆ�" msgctxt "current status of stack" msgid "Resume Failed" msgstr "æ�¢å¤�失败" msgctxt "current status of stack" msgid "Resume In Progress" msgstr "正在æ�¢å¤�中" msgid "Resume Stack" msgid_plural "Resume Stacks" msgstr[0] "æ�¢å¤�æ ˆ" msgid "Resumed Stack" msgid_plural "Resumed Stacks" msgstr[0] "å·²ç»�æ�¢å¤�的堆栈" msgid "Rollback" msgstr "回滚" msgctxt "current status of stack" msgid "Rollback Complete" msgstr "回滚完æˆ�" msgctxt "current status of stack" msgid "Rollback Failed" msgstr "回滚失败" msgctxt "current status of stack" msgid "Rollback In Progress" msgstr "正在回滚" msgid "Rollback On Failure" msgstr "失败回滚" msgid "Save Draft" msgstr "ä¿�å­˜è�‰ç¨¿" msgid "Select Template" msgstr "选择模æ�¿" msgid "Select a new template to preview a stack." msgstr "é‡�新选择模æ�¿æ�¥é¢„览堆栈" msgid "Select a new template to re-launch a stack." msgstr "é‡�新选择模æ�¿æ�¥åˆ›å»ºå †æ ˆ" msgid "Select a template to launch a stack." msgstr "选择模æ�¿ä»¥å�¯åŠ¨å †æ ˆã€‚" msgctxt "current status of stack" msgid "Snapshot Complete" msgstr "快照完æˆ�" msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "生æˆ�快照失败" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "正在生æˆ�å¿«ç…§" msgid "Stack Events" msgstr "堆栈事件" msgid "Stack ID" msgstr "堆栈标识" msgid "Stack ID =" msgstr "堆栈 ID =" msgid "Stack Name" msgstr "堆栈å��ç§°" msgid "Stack Name =" msgstr "堆栈å��ç§°=" msgid "Stack Parameters" msgstr "堆栈å�‚æ•°" msgid "Stack Preview" msgstr "堆栈预览" msgid "Stack Resource" msgstr "堆栈资æº�" msgid "Stack Resource ID" msgstr "堆栈资æº� ID" msgid "Stack Resource Type" msgstr "堆栈资æº�类型" msgid "Stack Resources" msgstr "堆栈资æº�" msgid "Stack creation started." msgstr "堆栈创建开始。" msgid "Stack creation timeout in minutes." msgstr "堆栈创建超时,以分钟计。" msgid "Stack update started." msgstr "开始更新堆栈。" msgid "Stack updating timeout in minutes." msgstr "堆栈更新超时,以分钟计。" msgid "Stacks" msgstr "堆栈" msgid "Status" msgstr "状æ€�" msgid "Status =" msgstr "状æ€� =" msgid "Status Reason" msgstr "状æ€�原因" msgctxt "current status of stack" msgid "Suspend Complete" msgstr "挂起完æˆ�" msgctxt "current status of stack" msgid "Suspend Failed" msgstr "挂起失败" msgctxt "current status of stack" msgid "Suspend In Progress" msgstr "正在挂起" msgid "Suspend Stack" msgid_plural "Suspend Stacks" msgstr[0] "挂起栈" msgid "Suspended Stack" msgid_plural "Suspended Stacks" msgstr[0] "å·²ç»�挂起的堆栈" msgid "Template" msgstr "模æ�¿" msgid "Template Data" msgstr "模æ�¿æ•°æ�®" msgid "Template File" msgstr "模æ�¿æ–‡ä»¶" msgid "Template Functions" msgstr "模æ�¿åŠŸèƒ½" msgid "Template Generator" msgstr "模æ�¿åˆ›å»ºè€…" msgid "Template Source" msgstr "模æ�¿æº�" msgid "Template URL" msgstr "模æ�¿ URL" msgid "Template Version" msgstr "模æ�¿ç‰ˆæœ¬" msgid "Template Versions" msgstr "模æ�¿ç‰ˆæœ¬" msgid "The raw contents of the environment file." msgstr "环境å�˜é‡�文件的原始内容" msgid "The raw contents of the template." msgstr "模æ�¿çš„原始内容。" #, python-format msgid "There was a problem parsing the %(prefix)s: %(error)s" msgstr "è§£æž�%(prefix)s æ—¶é�‡åˆ°é—®é¢˜ï¼š%(error)s" msgid "" "This is required for operations to be performed throughout the lifecycle of " "the stack" msgstr "这是堆栈生命周期中执行的æ“�作所必需的。" msgid "Time Since Event" msgstr "å�‘生事件å�Žçš„æ—¶é—´" msgid "Timeout" msgstr "è¶…æ—¶" msgid "Topology" msgstr "拓扑" msgid "Type" msgstr "类型" msgid "Type =" msgstr "类型 =" msgid "URL" msgstr "URL" #, python-format msgid "Unable to get events for stack \"%s\"." msgstr "无法获å�–堆栈 “%sâ€� 的事件。" #, python-format msgid "Unable to get functions for template version \"%s\"." msgstr "无法为模æ�¿ç‰ˆæœ¬ \"%s\" 获得功能。" #, python-format msgid "Unable to get resources for stack \"%s\"." msgstr "无法获å�–堆栈“%sâ€�的资æº�。" msgid "Unable to retrieve metadata." msgstr "无法检索元数æ�®ã€‚" msgid "Unable to retrieve resource type details." msgstr "无法检索资æº�类型详情。" msgid "Unable to retrieve resource." msgstr "无法检索资æº�。" msgid "Unable to retrieve stack resource types." msgstr "无法检索堆栈资æº�类型。" msgid "Unable to retrieve stack template." msgstr "无法检索堆栈模æ�¿ã€‚" msgid "Unable to retrieve stack." msgstr "无法检索堆栈。" msgid "Unable to retrieve template functions." msgstr "无法获得模æ�¿åŠŸèƒ½ã€‚" msgid "Unable to retrieve template versions." msgstr "无法检索模æ�¿ç‰ˆæœ¬" msgid "Update" msgstr "æ›´æ–°" msgctxt "current status of stack" msgid "Update Complete" msgstr "更新完æˆ�" msgctxt "current status of stack" msgid "Update Failed" msgstr "更新失败" msgctxt "current status of stack" msgid "Update In Progress" msgstr "正在更新" msgid "Update Stack" msgstr "更新堆栈" msgid "Update Stack Parameters" msgstr "更新堆栈å�‚æ•°" msgid "" "Update a stack with the provided values. Please note that any encrypted " "parameters, such as passwords, will be reset to default if you do not change " "them here." msgstr "" "使用æ��供的值æ�¥æ›´æ–°å †æ ˆã€‚请注æ„�任何加密å�‚数(例如,密ç �)将被é‡�置为缺çœ�值,如" "果您在此处ä¸�进行更改。" msgid "Updated" msgstr "已更新" msgid "Updating Timeout (minutes)" msgstr "更新超时(分钟)" msgid "" "Use one of the available template source options to specify the template to " "be used in changing this stack." msgstr "使用å�¯ç”¨æ¨¡æ�¿æº�选项中的一个æ�¥æŒ‡å®šç”¨æ�¥ä¿®æ”¹å †æ ˆçš„æ¨¡æ�¿ã€‚" msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "使用æŸ�个å�¯ç”¨æ¨¡æ�¿æº�选项æ�¥æŒ‡å®šè¦�在创建此堆栈时使用的模æ�¿ã€‚" msgid "" "Use one of the available template source options to specify the template to " "be used in previewing this stack." msgstr "使用å�¯ç”¨æ¨¡æ�¿æº�选项中的一个æ�¥æŒ‡å®šç”¨æ�¥é¢„览堆栈的模æ�¿ã€‚" msgid "Version" msgstr "版本" msgid "You must specify a template via one of the available sources." msgstr "您必须通过å�¯ç”¨æº�中的一个æ�¥æŒ‡å®šæ¨¡æ�¿ã€‚" msgid "environment" msgstr "环境" msgid "template" msgstr "模æ�¿" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.507496 heat_dashboard-16.0.0/heat_dashboard/locale/zh_Hans/LC_MESSAGES/djangojs.po0000664000175000017500000000505015257164523025067 0ustar00zuulzuul# Linda , 2016. #zanata # Tony , 2016. #zanata # Tony , 2017. #zanata # Tony , 2018. #zanata # Tony , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2020-12-23 15:26+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-03-20 03:41+0000\n" "Last-Translator: Tony \n" "Language-Team: Chinese (China)\n" "Language: zh_CN\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" msgid "Cancel" msgstr "å�–消" msgid "Check Stack" msgid_plural "Check Stacks" msgstr[0] "检查堆栈" #, python-format msgid "Checked Stack: %s." msgid_plural "Checked Stacks: %s." msgstr[0] "检查的堆栈: %s." msgid "Confirm Check Stack" msgid_plural "Confirm Check Stacks" msgstr[0] "确认检查堆栈" msgid "Confirm Delete Stack" msgid_plural "Confirm Delete Stacks" msgstr[0] "确认删除堆栈" msgid "Create Stack" msgstr "创建堆栈" msgid "Delete Resource" msgstr "删除资æº�" msgid "Delete Stack" msgid_plural "Delete Stacks" msgstr[0] "删除堆栈" #, python-format msgid "Deleted Stack: %s." msgid_plural "Deleted Stacks: %s." msgstr[0] "删除的堆栈: %s。" msgid "Download" msgstr "下载" msgid "Heat Orchestration Template" msgstr "Heat ç¼–é…�模æ�¿" msgid "OK" msgstr "OK" msgid "Save" msgstr "ä¿�å­˜" msgid "Saved Drafts" msgstr "ä¿�存的è�‰ç¨¿" msgid "Show More Properties" msgstr "显示所有属性" #, python-format msgid "Stack %s was successfully created." msgstr "堆栈 %s 已被æˆ�功创建。" #, python-format msgid "Unable to check Stack: %s." msgid_plural "Unable to check Stacks: %s." msgstr[0] "无法检查堆栈: %s." #, python-format msgid "Unable to delete Stack: %s." msgid_plural "Unable to delete Stacks: %s." msgstr[0] "无法删除堆栈: %s." #, python-format msgid "You are not allowed to check stacks: %s" msgstr "您ä¸�被å…�许检查堆栈:%s" #, python-format msgid "You are not allowed to delete stacks: %s" msgstr "您ä¸�被å…�许删除堆栈: %s" #, python-format msgid "You have selected \"%s\"." msgid_plural "You have selected \"%s\"." msgstr[0] "您已选择了 \"%s\"。" #, python-format msgid "You have selected \"%s\". Deleted stack is not recoverable." msgid_plural "You have selected \"%s\". Deleted stacks are not recoverable." msgstr[0] "您已ç»�选定了“%sâ€�。删除的堆栈无法被æ�¢å¤�。" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2652628 heat_dashboard-16.0.0/heat_dashboard/static/0000775000175000017500000000000015257164606017603 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2652628 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/0000775000175000017500000000000015257164606021532 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2652628 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/0000775000175000017500000000000015257164606023200 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2662628 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/0000775000175000017500000000000015257164606026130 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.347261 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/0000775000175000017500000000000015257164606026704 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-gray.gif0000664000175000017500000001056115257164523031430 0ustar00zuulzuulGIF89a,,ôøøø€€€‘‘‘ŠŠŠŠ²˜˜»¥‡°–‘·Ÿ“¸ ™¼¦ñññ™™™�¶�ÑÑÑÊÊÊáááéééÙÙÙ©©©Â¡¡¡ÐßÕººº!ù2!ÿ NETSCAPE2.0,,,þ Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°8—0Îè´zM»ßßÄzN¯Û ¸~odÜÿ€�h|„…3‚‰Š‚†�Ž‹’“��–b”™š€—�V› ¡ž¤K¢§¨u¥«A©®¯k¬²8°l6fµi³¼/µI¹®½Ã%©TÂij§^§ÇˤÁ™oÓ”mÑ�›†ˆšyÚ„r™—ß”áâoš¬×Šëbܔ潙ñΓõÑ÷øX”þP‚Pʤ‚#ÐÁCèDÒ>†#")b1 ¹DÕ*žPH�ÆVŠ~4!i$�E&þY,ò˜ÒÊ–*_¤!s抚6[\¬”ÓW¢ž-}‘ìçÐF�ƪÔO ŒJg$:5*MF@Aµê«M¯\qåsç�°>xš4« íµÁºí1ÖÅŠpçž$‹0¯Þ½£ úý ø¬¿@" 6¼n°b ïè4æû؈cY—+ƒ < P6ÍI2w¢ :4éRlc•nâ™ãÕLN�~ ;6gO·k³Îýˆ·nÛ´ýÙú»¡o>²‹·ãÈ)såSŽÃ‘}¹äBÔ«/á8‡PvíK"«Ùã¼-x*âéù~ÞTp0ìÛ»Žé½|ƒvw‰_ ÿèöõE€ÿü' iôqþaà�^§ � B± F(a…O`ha† ~Òá†WLX„ˆ "¡!…敨ʼnG°¨ân:‘Ú./>˜â…7ÖˆwãE1£z  ÚG.næ`_,P‘D9Î7 1%XŒ v7eI1y¤“CÖÏ•[VÙe<  ”Þ0ÎAœ8¢ ÙäˆT.ƒ ˜aÂYg4BÎIg�Hœ 8P�$§�¡å˜lÞ1Á �ˆÛ›‹1zh<�‚ ÄÉOŸá)1v@°‚Ô¡d@óܙ֨½¤J‡-Dkg”öp+/˜¾ A®rîjC]–^:‡/DꪱV Qd)u4ðÀþKG¢oU‹ë (àí·à†û€¶Û.K‹°®Áº2°‹Ù/Qºë“¹Ö¾‚¹íÊ»Š.÷Ò{•¿¤Â/¾0軯À7»Ã³¬ðÛ¯Â:|°ºC|.ÀåR¼.ÁAqܰ.|îIcÄ[²Ç[ÜÆ«Èz2üY2 3ò²¡³¬Â��y3 àS³@(÷ösUõ›¯Î&-eÒ!×0ôtN㔳ÊKcýqÕVÛS4Íuè·5×]§«õ¼g“Bö—W«!6ÚJ«½6Û^3]Ì×{Ì�IÐmG�uܳé Pßi¬ù‚Ÿ¼ ÞÎ0‹ú wP&®øâu§­ÞTOžÉª’[žæþnh^få€_nwÞ¢wÓy韟žyêt¯Ò8‘ÿ=ò*�®ºÙ¬› ^¨Ðê¾û»vƒ^Ÿð™ÄÌ»ã1yn3òçÜûÝÎc=åcOvõÞ]Ÿüò·?dðÞÇîI«Ú�`üåc_ �hÚ>¾ÜíOÂwöi`Û1÷zäYÿAÄãß�ð÷?¤MJgèKðWÀ²]‚]u¨ýø´òo}ù  "8G¾ ºN .Ó È1 ÚH„#lšÎL¸:Ðh+ü`\XAÆ�¯£¡¤ŽçA²C‡¢N e âÌ$"ûŒ¸Ãч¡c"âxÃôåPŠV¼bøš—E*bŠ^Lþ WôE%.±Šj0 ËÈ<òO<d çgÁôÁ¯gœcü¤×F 0�z  ÇX =ÒQŽ}D!CdÈCÚÐK1ã É b¥�%ûë")¹ŠôHE’-lä'˜ÅEzH”täM@IFTòq‹™L# ç˜ÊQÅ”T¥- ËXöRˆ®$ÅìT¹J01˜uT&]€K!“—Ä,¦&�ÙÈWFÓtÆA©HCJÒ‘š´¤(=©JSÊÒ•º´¥0Ué>ªPšnÁºÄ§‰0èQ.åT§vêhOwúÓ#j¢ÛËæ<‹Úıp¨EÀ)S›jœ§BÕYS­aÕô*!„Y bv‡ÕAî[Ø*\•®+cì]ÇúXÈþK²~¥l3yÖJÌb©³€ÅæôçLÏ¢u©¡õfb/fZApа©-$ÏÙÚ;Ü�¶±U­R‡PÛ?Ü·kíê7CÙÛ9ü¶¹}h^QTÜn:uµŠ¬iþ3– 4¸?P«s¹Ù&Ÿ 6©»E˜ÞO~¸Í˜ÌyÁ›Ü†¦·QëUŸe‰[]õÒž-š¯fë _èúr¹3Åq© `¾–× îƒZ�…ËUònⵄ+\.õ‹…ÅŽ®¿ÎÁ0 ¾Ì~ªøµN{�˜`bÜÑžO81µñÖ’nÄ7˜b€ÿ <ûú—<[±8À*ã€.8¨7–èì`k¸Ç�l�cŒä$›wÉLn²“Å÷c1yÊ"�²•«Œeåº-Œî²|)\W-‹9Ëföé¦{f\»“ ³“‡ gúBDo®³�å\ÑP¥-ÎPßG¬À tiP]jUW&�îĨuŒåYÓºÃW^�Š?ÝjW»ÅÖ6VtXâªà9¼mCÄþp¤a“ì"ß9*ˆÙ«¡chMøÙ_™¶³ù‚åû�ô$ûwƒšm)䋃0á‹=þ^$ÆžŠö4ÄHâ"ÇŒþ?vÔ'dž’! 2Béê$K“…^‚P9IæD>6E�Ì9îÍÆœ\Ú ´Ñ’‹¦8ʧR*N1ÒÔô´EÌŠSÇTµ¸ÒaÔ­-²j¸ï*ØN]é™=‹¶&¼µlÛžy 'b\¹sÃÁ½ã™m{ùÊ,а`¼TgÁ!{ø†áFiçxüGìÉ=�r‹™GdGœ;{}ˆ´hK¦û¤>­ã³Ÿ¬ë­Vã:öè¼vzþµ=d67ܼe›y Ænºíão">}kôçá»/ÒþGýù“å'¹�ÿüúõþGßz‚' ;ö–à�DüGƒƒ :¶ }F¨ �Ah¡zæ`Y²×¡#†rÏñ¡‰"bxÛn,.�‹ï‘a@Œ-˜Y‰8þ0£Žä¥Ø#ÚPä�Ô9ƒ?!Y �Òèä“PÖÀã”@\©Bu b™ãóÔðc(^ÆG¦‘Z–¹#Œ &¥šED·Š•iÂùâ™K¾igƒu¦Ôçž5J‡˜�€*aF˜éY¨pU® ä¢ŒâéŸ�’¨¨Q—Vše¦:Qª)š�’Âé§k†j‚§¤ÒiêH£¦äœ, êjž­Â$é¬ ºT­¸Zªë �öZê­§ò*,¨«‚ ë±\þ%ë�eÌ"èlËF;©±Tk­UØR í¶Žv;Á·àî:­f˜W.…çf@–¬¸ð^ÛîM÷ÖKë¯!¼«/«c4É—ÿ 1&¡ýÚXð…ü•ïƒZ×iÃJ¬ÃWìëÅÔ>¬q cqãô~oÆ8צɢŒAÉ,û0Ì1Ì1º×l®ËøÞ¬³›3çü³ r^A²ÐC“€¢Ñûœt¢c Ú1ÏOÇÚªXUoŒ0E¯œu³T�«ðת†-Ído %Úi»Û*Ûm÷ìt-HÇmëÖj×m7Î÷½·Ø}ëý·Üxó=÷àM¸âˆ·¸Ù�O ¹ßƒ¯-xäþg¿}9æ–Œ¹ Z:7òçóÞ»t—¤Ë 9Ö©«Îxæ�·Nw¾”ï]†×†.;b üòæˆWwtì»ÇÃkíq׉|ÛÊ“á{ñÙòÊ:ôo¼õ©‡,±²ÀÛ^Æó¿cOúŸËM~÷vŸ?îÔ{‹-ÁígP4Ó6ë?“®Ûß¾§þš/nÿUCÕüP?÷é €I“‡¦Àòé¬:ìsúžV­Ö¬‚Œ'ÈÀ rPgß² ÉB˜A�‹{%ÄL®ó={‰/mï!Ä åÂ×yÏo'¬\ídX°þñP_9Lœ a¸¼®+ˆÃûàÂ�(¶Î‚‡z"�h-š!‚̳"þ�õÃ+rΈ[Ä£ˆ¸îÀ‰7Tbî†8Â02±�)$œþ:ÇèÕñSgß¾^Ä0P�³z#ØøxD?ÎÎyþk!ù-AF X¬XsÅÈc�Ž~D¸$/hÈ5α‡Ž¤ÁŠ@ØÉ¼•2FB Õ”Ê(ÝqH¡lÙ+QKYVrQµ|Õ'¹˜®ßœ?­Ö*YL~œAjÌà&}9KühV,üev”¹Lf“AÔ$Ž4yó¡j‚¡›Ì*&•®éž7 S9â4Qmè�MR=š†X'œ¨éM?ÈÓKšÄ$%îé$ÊØ3�éñçøIKs΂žjʧ>7CP ½žæhè�þZO†”5_ FF§N�Æ£ÞÉ(`@Êœ�þƒ)î9Ii!Ò�®t-�ÎK5:SÁÄ” 7= J+"ŽÚf§ªd„ŠŠÀ§CÍ©Hˆ –�Å©lQ)S_Õ§HU©9 ÉS´—n²±,\å Ed2VÑ”õ#‚ˆd\ìÔ°ÆfÀ)[e:µZ"åTÇHçš×wÀ4 ZÄ(¼ºÍ¶ÃWõ+1ûÏnÀzÜO$Êt‰¸š‰±pÊ„º~@ت²òv­!&R¥Š ² 8€Q;«ÕF€mC`Úº6–eVbKË[¾‚«³½ímh{uÛàŽVƒÆímÖ€-›ÜšV¬¸Í]hã  Šþ‘·ÖÁnó�Ûìz÷»à ¯xÇKÞòš÷¼èM¯2";././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-gray.svg0000664000175000017500000000434515257164523031465 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-green.svg0000664000175000017500000000434515257164523031623 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-red.svg0000664000175000017500000000434515257164523031275 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000021200000000000010210 xustar00111 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/apple-touch-icon.png 27 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/apple-touch-icon.pn0000664000175000017500000000163315257164523032413 0ustar00zuulzuul‰PNG  IHDR´´ ögAMA±� üa cHRMz&€„ú€èu0ê`:˜pœºQ<ðPLTEÿÿÿÿþþþõöüëíûåèþùùøÏÔï–¡éhxäFYà1Gà/Dà0EøÎÓþøùöÄÊéiyá2HÝ4Ý2èfvüéìí„‘à,BúÛßèarÝ4úßâæUgýñòékzðœ§Þ 8ãBUî�›üèêäL_÷ÆÌß%<ñ ªÝ3쀎ÿüýên~én}Þ5èbrì~‹ï“Ÿð›¦ß'=ëw†ýôõß*@ñž¨Þ"9ð˜£þûûç]nýðñÞ7ò«³â9Nù×ÛãEYô²ºí…’íƒ�ýòôÿýýþûüï‘�åQcåRdåPbês‚üîðém|ç_pà0FÚ í»*IDATxÚíÝÙRÂ@@Ñ JTP¢€ ¢âwqE\pßþÿo|õiËIèÖ{ çÁJ¥zÚ"""""Ùe²#£Ãm$›ùÙË��çý‰Éá5áçÇǦ¼øäBqz¦ ¿’?[,Ä4Ï•+�”*å¹Xæê|-�Sm¾Ã\_dµP�47i-6¢ÞtÍ%qè¥fÄ»¯¾Èk9âd¥&][±š[«�ÄV[6ôÚºHôúš ½±)½¹a{wl2Û²¼?Âm¡èíЂÞŠÞ 4hРAƒ 4èÿ‚.íþ¾Rªhoÿàð÷ìïùi¡�ڹ㌛ƒ�ã\û(ôÉ©gÜå�ž¤€>;ï8=F뜟%�î^8>ü»è&޾ì9?²ì]&�¾ê;G÷¯’F_·œ£[×I£ó  ó Aƒþþ-pS¾µw×8GšwO-ßd iíþ!´÷&ðÔ0ò©÷6óãÓ³½—×7çæ·×—ˆ§>=ZÔá»Ðo�w¾ò@ƒ 4hРAƒ 4hРAƒ úO£5þZåI€Ê3""’�Ê_‹ñ•´´Ê_ª«œ P9}¡rÎEçD‘ÊÙ-§Srƒ´¦ä\Î#~|2ùÉ`0hРAƒ 4hРA;Dk¼AVå]½*oEÖyÿ´Ê›¾UÞ©®óöz•{TndйûBç–�û\Œ)g}›sf¦coÎ1ÆxS"vå~xº£p©î , YÞ5NIEND®B`‚././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-gray.gif0000664000175000017500000001304115257164523031575 0ustar00zuulzuulGIF89a,,õÿÿÿ€€€ÒáØ¸ÐÁÂØÊ“¸ ÉÛЊ²˜‡°–€«�Ïàհ˺¤Ã° À««ÈµŸÀªÛèà�¶žàê䘻¥·Ð¿®É¸¿¿¿›½¨¶ÐÀÐàÕØäÜÀÕȨųÛèß¿ÕÈÈÚψ°—ÀÕǶÏÀ’· €«�!ù2!ÿ NETSCAPE2.0,,,þ@€pH,�ȤrÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~Ïïûÿ€�‚ƒ„…†x ŠŠ�‹ ‡—˜w  ¢£¤¥¦¤™°±_§·¸¹¸¯²¿ÀKºÅÆÇ¨Á̰¶ÈÑÒÈÊÍ×~ÓÜÝѾØâqÞæçÈ ãìgÐèñò¹ í÷]óûüºöø§ôHðÔ�€—l+h*…,5Ñ@€AP MeHȆŒ ¬C C(‚vl7PA‰oÀã·? ôhЯ&þ°y8$€@ƒ“èøÌ$�€¡†*ˆW`i¡�çX€ k�…©ç X�‚�k íö`잙ܜ½á\N·vÌ©L8@/Þ9 ¹5°jîîß6Þþ¹ËíðšÀÑ(þ»�±i—Á­ÀdÍ 0Ž64¬‘˜>€[‰Õ\ p«‰ÔÑh×¶bašØÝIps|Êåh\‹#É0­ªò&Â¥1xÞDŸ´pÔ�”‹– Üb˺‘¦A|iÌËFæÜ|ÑÚ?§ÀÝ}•íǨǷ�yñhåñ‡Åw¸t°[}ja]1J­†Œn r� hFØ�§d†2þXgÆìEØ1ªy|ÆH02s™xâ1"v4Œ.’1¡OÇLW#kå’Bùíh}ÆÀP�BšÑ—1Ÿ±Ä`’i@"@Æü%£á8Æ@x% )Ž1%~©ŠÅŒ3Ú-eš©Æzi^³ .:ºÉ†”Æ4cL[vºñ�1ă!)òõÉÆ’º´ËŸqúš¹ü’§£q0§K¡‡LJiDæà%ˆâÒä¦m¬y &aêÂ'©rôxj¦�²Úª.¥¢©¬sÄÈœ·àz‡®~ô¦Ë�¾Æ.1ö1è(ÅþªKxÁÓf³r@zŠ[êÁ­ÔÎq,.~@Ök·wðj ¦þvà‡Ë§äÖQÌHxl«K�íæ¥‹ÀÖKž¸XYGº©¯½‘:[°%œQ®÷Öáj)~$ ðÄ ÃÑZ.ØÁ1c.Óâ!1Å G¾ˆ5ÌGÈ(‡?r|ûj)Ǽ¦B¼²É'ˬó97ãÒñ™8ç¼óÐÓÄqܸnè¢âD7]aÒ¹d›ÆÆà’åôÕA·qô)£Qó(Z�õئ0LgÉU‹]6GÃñ0)Ä–!B.èî‘6�w?ªËÏ6æ/ÓHãøÈϦ!ï-µªmsBmÃÁI.^’ÁÛ¢ü øË‚c.=h¨jëàTæÇÅl*9ì皇zϧ4Ø7.‰":þ>�Ë!n)q{p!³ßSûæ»�a.)a£¾6㽞ü§ó¾ºïË»ª)|kAõ󗟹öî⢀Ë*:Hôc’�6öYd-ˆù×ü^m.þram)š:í쳡~”€IþÌpŸx‚ hIH€Šó_MÈ:S$ p:…øŒ7"ô5�{üc Ó6À5Lw]ØßÀ’ Aå�¢nTð@.F5Bvp "œB§î×BZpt%| 1XCÒp_9tB ™²¸'1{<$Ø)"g…¨éé†ãù!‚µ"�tR4D1.à„�Ý‚‹0³¢Ô•D) Ñx9äÖŨ¥¯_¿ð"Éþ¢¨F%–±{·ÔÜ( 9žñFøbc†Ç¬+|�Àðc Ÿf°,Î�_Žd²‚ÁÈ!(’Šúd0y„\<ð•€4„I౩kPȽbQ´"´2�P”ÃIq©¥R“z¸dã.0JRÞ±ÈEì Á_Ú�¢ì¥Ýpy†TÝÂuR ¤(ž8±0ÆÒ”×4Bÿ¨Y1k–R[+…oy Ô/ºì‰7�Ii@`F¼…@Ð>…!Œ™g8ä(,@\䎕ö\ç÷ÉsMÁeˆ´I@}9ÐeZ‘Œ ]ß(À¨„j 4¢~'FŸ ÑiJ”†é¤IEjN¾�€ÙÂÛ þ1c€1¤êÂÇ`R7 ô›t4Eñ�H6dvŸùÌÅ™à³�ö4”¯Ãiô)Šêù°C…åQ•ùӔ¡¤&Q­ú^Nõ•UUªãè…ñ“0ý*W÷Ô3Ün£L‘Xß ÖÆl²­føšGŸ`©¹V‘ HèfXÏÚNLÞ”¤kìÒjŽ»®Õ …ëfYHžÆƒœ1-)^ËÀT>á­`)CÑaÆÌjö±mèìyn±4ÙÉ£Ÿ",bóPLqb¶ñûcÏ8ÒËÂp³d¨-)PùTRèq|Þ¸Ânkªæ¶«£°Üp±*Bx¬°Mî_ ;áŽâŸ�¼Eu ±þÜÊVA»t x&áz¥X¥}z^iP4½¨eƒwEáÞâŽB¾ó­£@�úÛü®a~¤`áÌZ £Ÿ.°_ÝÀÞ[8ظ`â1�›SÞ:vÂm@ð( Ãø’BÃöðV¼ÝÙ"B¨·%?aAßì²8“.¢z�b¼)žiŒsq_rWû x1œGPF8žO>‡×]Q,Ù•Mr”Û� J– ²(Pxѯ¶Ëø±~Ãü„·µ×E/Ê›ãúÖ À¤^�Ê3;÷YÂGŽCA]lÊîõžó°±o?h‹a2²‡-i£@çƒaÊcÄ$ MþÅ QeŒætêÒÁý&0ÑMY¨¿¬‡IBAÄžEô@Š,Ó…­Ú¼~ Ü÷ÎÜè×UÈ_Àô­EñÉÞšâÊhýõ–xg3 š@^±),Ue ›×½nóGáÔÀÞ‚Ì�´¶z§;n0ëÄU ø,nQë®Ü_€t¤� ov�ë«8V³::ï&˜:ºô¯§k o�÷Û¡kõ'Ÿ™PinÐZ¦ŠÌw¶×¼Ÿ‚]‰\(Q}�k>T9Å&÷)ÐßL4»y¸¶6_E“"¹¨`«=ñ5¸“ÇKØf-šrË!Ìe9«�î8Ñœw¶jÜB|iK' Gãá?|('ÚÈ�7.osy~êÿfaíw¿„™;ìsc3dJׂÔCßö½{ØP¶u ëniSOR°OfîS-wY_^÷ÍÆ|í¡ü�»Vž[põÎkÉKŸžªÝƒŒ��MÁ‹4UõƒkÏû‚SÈøö¸¥zC�?�•î Ív!�ÚÊ—¾*ûdÚíÝÏ¿‡ä%í�ýw^ǵzÅzÇ×~‰µh‡@{ÃõIþm¨xxþ÷XuÞ—}ù'¯Ec¸°S]0€i×sñ0�H€›w�$|+ØX2Ç‚oƒ²�tðtÓ`‚€ €£PrZÐY»f(8'GƒÈå}‰â<#øC„‚ÐYÔv!¹Py¾wØ„r¢G|×T@8w»÷J§U`Ð?RÈ\ˆ…Ff éæ�gE~Lèe]H�`¨�ÞS„5Wf z8Ë‚b4G„ph†gX h�'ˆF§ˆnàv¿àN@O€f €�TXvqx‚1gJ÷†Y—„Ìw€LžÂqÜöSx/xBo°zžX…—ˆt¢‰7 ãþ4‰¸uvíŠf“eoP‹Ø‹»¨‚l`w¯˜T¬X†Ã¨PG¥w>Ãx‡|@jô‰Ï¤ŒÖ†ƒWØŒr`Œ}8ˆí¦&ψ‡ã8lâ6�vÀ´7{°„Dã$„Èwñ¨î(3÷À†°“‰¸O=‹Ò(\ÇGý�§p‹v0€Á8�` ƒ£ðfy0}¼¨�IàŠÇøŒ"Ly�€pnë’‘dðq 7ÔHŒý˜‰r°�Œ’–‘d ’ðƒ,¹ sèiK3Iü¶’úX ‡¸“TP“‰�% &”L@”°€� ~JÙË" °Š2ØB 9 :©e.Ù.G“þÀЉ •싃0L•KГ>‰z–¦”ý3 i˜ b©djiDO( ùNw©wðÓ_éq—4BL¢–Ç s yIb,™—vy$‰9“NielC&‘y 0) ^7�ÈP• " ‰ ‰¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-green.svg0000664000175000017500000000577515257164523032004 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-red.svg0000664000175000017500000000577515257164523031456 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.508496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/db-gray.gif0000664000175000017500000002630215257164523030721 0ustar00zuulzuulGIF89a\\÷ÛÛÛ÷÷÷õõõºººÓÓÓ×××ÉÉɾ¾¾âããÍÍÍñññòóóêëëÒÒÒïïïßßßÝÝÝëëëçççÆÇǾ¿¿ÎÏÏËË˵µµÃÃÃáááÖ××°°°úúúæææöööùùùûûûøøøþþþôôôüüüýýýÚÛÛÁÂÂÀÀÀÑÑÑ···ÙÙÙÖÖÖ¸¸¸óóóŸŸŸ»¼¼ÞÞÞ²²²ËÌÌììì«««¦¦¦±²²ÅÅÅÌÌÌ™™™ßààååå«­­ÆÆÆïððÙÚÚðññåææÑÒÒàààרØêêêÜÜÜîîîãããÔÔÔèèèÊÊÊíííòòòéééúûûîïïôõõððð¥¥¥ØØØ»»»ÈÉÉ¿ÀÀ¶¶¶º»»ÇÇǶ··ÁÁÁÃÄÄÜÝÝÀÁÁ´µµŽŽŽªªª©ªª•••çèèÅÆÆ¢¤¤¿¿¿ÎÎΜœœ¯°°¢¢¢èééãääÈÈȼ¼¼ÏÐÐÕÖÖíîîàááñòòäääâââÄÄÄÕÕÕÚÚÚÝÞÞáââÿÿÿ’’’‚‚‚©©©³´´ÒÓÓ¬­­±±±ìíí§¨¨™››‹‹‹ ¢¢¹¹¹ ¡¡¦¨¨ÌÍÍ­­­­®®¹ºº!ÿ NETSCAPE2.0!ÿ XMP DataXMPA?xpackeA9C02C3" xmpMM:InstanceID="xmp.iid:Ba> !ù2,\\ÿH° Áƒ*\Ȱ¡Ã‡#JœH¢�‹/ҨȱãBƒÀBD²¤I’ŒM´Ñ£ËˆVDbt²¦Í’ŒÀ¬øð²'B™7I6D´¨ šA#íðéóƒ#¤&EJ ĉŸ«X³ú1´#ÁÈš�ñdZÑ)T¡ i]Ë«� �N2r4–,ÄAgsªmË—¯0'­°ë�$“�|è˸ï‡g9!¬ÐQÔ�33~ •Ñ Ê?|%9a±æÓ|�D2©.eP AM»ï¨�Z^ŒéÚÀ׊Ʃ›é “+‚+o›ç`ã8÷.Ÿž•wÉç/�£”N½ûV¨B^îÿ(Ùˆ»÷î†�2*Nööóð¯¦GéZâ‡Ãˆ™�ß]HIH­FÒü¨"Pt fIáÁ„” 6øÁ#BÕÇ�€ŒüVa|†”äˆE×}Xas$MæÐWšha\ˆ€áÐ�û¹xÞx6„ÞMAD]ÒÆ�mò‘mÒDLÑÝW2.ä_ŠËýX$ Xf©å–[&K(7%"*&$`$ÁM±H"\¶éf›tp"¨�%ÍF[‹¼éçŸY&ÒEmÚ1¢Ð B§ ŒþYHmH-…~ƒÔFD£˜¾YÛƒQ„'•µ-šé¨X.Rކ¤Ý#Á©™šÿ RôàË-Q›°º™È"N÷àˆÁ¨çtU,"j¯m,BuÚ ZIü‘B… ù—: ©daž"IMÉ*p.„àb�™•T¦@–!‚&pB(’€ººXp_Õ /I Ö6&;àK]@hA-pÃôUrôÖDÈ@apf!!5‡"±õ«ÇA!BQ`$ òÊZuHɇ¨¼C¨\ ìsɸ7¥´!ÅîiBä¼óÑŠ€áÂr?´ðÁ +rôM‡€ÄÅ>“ô@O‹ÛUËRuˆ )ï°ôyMãÐŒ¥K›l³·_Z׌ȱoÛ‹õy!4ÿ—5"[ $rĈ$�7u;<ˆˆÌ$Eb7ĵ�[’"ZPµwc.t5Zר}\P¼×FòMDM°òé+‹4ºM=oJÈÿÊ{³ÑS×ÞzmýdîtBl^{ÉJ/çnApœ ƒ¬þ{IZ r9j’d,ßB$ ÒΊ�=ÈáËE{Эè>¯• e$~Vs³Õ°A«Ò«H¥ ö½8¤9zê®À$)’|†€ö©(I¥"w’ª!}r‚ÿNâº%df�íNB•U¬|„Ê¡<“ÜN3‘JȧîV›(ny; �f µ�3ùÌw(¬‰ ¨•:%H!c²Jp 1�—Ű$„HÀÙÿð—"†ìˆ:.Â6�e/f›”B£ø`Dƒ+» 3ƒ£»­Hk6*Ð`Ô)†Œ ~a<ŠÈé0�Ô‘ìò� Á‘:Ê�DtÇå(Î_m, �úH(•?ú!$jÆ ޏ'?[¼ã|ò£!‰t1‘ŠìË$ד�èd²-“ôbO„ÉObÅ:$ÁÎ(‘cÊ«¬‘ªô *cä¡;%ìaÊ#!BÞæ?¹$Ë-ISË©†5•$L¼P¢q'ŸÍA ƒ˜fòç™%‘Œ4•0D²/ L,·y³f>ƒ‹\èBN‡˜S.S© c¸â›„%™íTP‚2,£å,5QJ>9“™ü0';hO@r=˜¥d%ÁT(S0¨‰Zô¢ÍhA!ù,''ÿHPA>dŒY8F̆cZøP“DÁ‹6ñ£Ç�ËÈPc£@iÊ€\ÉråÓ´œI³c˜ÚÔÜI³Ež@[æ J´c�2Š=:ÐH •JY–IcÑ$€4:£Š“&‰É¯I”øh±p È6 %(ùuć¯pãÂEb†Lƒ·ró¶=ƒÆ š¿gàÕ+÷Ã0û&NfÆ3ƒõ~0£áL˜Å\ÆŒ9Œ-gˆ æ3Θgø€µëׯU4Ó¶mÎdÎŒ0i ™Û°Ùœùy.‚¥¿fº‚Ý&Ýž�dº@ºv‹[/Ü@1šÜf¶ËªEÀ÷/ÌdÀ4€,~Äq-œ5ÃþͺuëÔ¬vM{ó�qM¾QÛµŠ; ÿàÁ‚4ïÖ*>³à‘cóÈ ~DhïôëX(îq2v¹<ü¡þqù†Þ¯ ~bsfמSˆž/zÆ Í¬aÏîÍ1l� ´×¸™$€ ø x™&Æ öõvÃg&¤wÑs|1 á† wWƒß¦`Ü¥ ]ˆ&ý0Ãqä-öÛ'b÷Ã+^À~—yÚcnhBh÷½&�x ö—Wl _Œý�‚iJ. ¸C 'WÛ„ËU �†?ŠÆ#“r!ù,''ÿHpÄ’ ºXYh…ʆV¶4¨²dÁ‹6¤Â±£Ç�kt©‚£À9j€\Érå†~ÈѲ¦ÍŽ5Ìr³§Í-K| m €çУsøÐéP¥‘lQé”e�M&Éa´ª•KLŠ]¡Á–…@fY(1II±8ˆ�Kw®‚&ZLÈ­Ëî.M¸²doߺ’$ ˜1c/’41Ü—C“C¼8 @óæÍ^®\b¢´é¢±|Þ¬‡Å«cË–Ýz`سsÖ2ăÉÄ ´èž�eHÊv—˜@-\6–Ñ&–ø6÷°mÖÞÍ‹<;b�¹ð­nâ½î’¿‚lÖ’ÀÄäò”_ùÜy¶h ¦ó›rÅsìÖ¯ 'àjµ ÔÄ| Øuø—àf <ÐF–™ÀßpZŒöyÔMˆ‘M„(bˆ^´ÝåYÇx�‰§v)Š¥ÀÍ©çXqIÀXžIи¸}&Zi’�(â¥õG›kè=8  è¤nWèH�¨6¥c^˜0]$¡ÁÍé–at_–wd~EZ9W@!ù,''ÿ=HP@„ `XˆaÆ€AÁ‹6ÜÀ±£Ç�/€ £@ .€\Érå€rhв¦ÍŽ V›@vÐW �½ù%Á r„ÝtÙý —i�G]_!Ø!âˆ"VHÐvÀ—à &^Ø©h×Í©÷Xq ÄŸ ÐÈr6ši“‘8¢¦ùGÛRèEXà%è¤nsèxb«MùwLGW Ô1Gsºm�—ð©_‘VÎ!ù,''ÿH0„‚$IV(Y¨ÄG†JŽ$‘  Á‹6ôÁ±£Ç�™¬� £À˜€\Ére‚$$’xв¦ÍŽL’Lq³§Í# | m €çУ ±éP¥Qé”%“M"y`´ª’ LŠU`$É‘… @¦X(II±bŠ�KW¬‡J–È­Ë×äCF Œ ÈÞ¾tI !28pãÆ@„ q˜/‰y<ÀfΜ�@€€`‰éÓDFÍYÈRƬcË–ízàسsƒV‚àƒIÅD”èž]’Êvƒ,I-\vÒK‚øö‹¼îˆÞÍ[={â%އð¬á�oÀƒ=pVBd åò>(‡ÚóìÑBNë?�ÂçØ®‘€Ûp>V›@#ÐW �½ùÁ rFDÝtÙý §i�G]_Œ âˆ"VHÐvÀ— à &žØ©hÍ©÷XqHÄŸHÐÈr6ši“‘8b¦ùGÛRèEXà%è¤nèx¢«MùKLG×HAsºm�—ð©_‘VÎ!ù,''ÿH�È'OˆÄX#E†1’<‰0‚Á‹6LÁ±£Ç�õ‰à£@<ô€\Ére�'y´œI³£˜öÔÜI3Éž@[æ J´#�DŠ=:ÐC•JYêáaÑä:£¦ˆÁc„ɯ#�±W?p£@pˆMBãVý€s“µ1&wì!„c�së>Ÿ Éa³S79b¯ß”ùt›h¢Ý1àw0[^½ÙÈç÷ŸÝܹœºõëÜø·~œ>?~Ú&�à}þQÖ�‚yЄ|¹ñÁ™å ´\^Tha…¾Ñ…àv‚Y×vF8Ç¡I!¸1xˆí¦€ˆÔ… €‰”pe›yÆØ…:àÙ|¬ ä]�ùÙÕT@²Ç"A!1Z‘ˆõÑÄ„*`ÄÃÅæàqP2W¡ŽŸÝxä[!ù,''ÿHD$H$tXØB†Œ ™Á‹6„À±£Ç�H˜£@4€\Ére$i´œI³ã˜’ÔÜIÓHž@[æ J´# �Š=:0„•JY> aÑä:£Bè@#€É¯œ 1²0È$ %Ž(ù•D‰¯pãÂåBˆ“·ró¶Eò&Ä›¿HàÕ+·„û&NìfŠ€ƒõ–ቛÅ\ƌ٠HœˆMã³Θ§(�µëׯU7m »6g!HH˜4LCˆmØ�ˆ<7li߯%€v@·I·„›rˆ.�®]âÔ ;Qü7Ûìp&¦æ€Y '�Á“Ë€³fØŸ§Œž? ƒÍ®U³þ͵l�!´×¹=ç~bFƒ Øa4™XÖ&h.|wty‘†nØ FÖ¹^t%lçWwSL7âW �y‹'€Šàq €‹˜@gŸ‰öX‡º Ú}±­ÆW‚:AP€HÚÆ�q0ÅiM.æ†ÎÅE‚S0€œm2—%xAÎ÷#”p!ù,''ÿH°Ä. YˆdI†H¸ð¡Á‹6\±£Ç�Ÿ8£ÀNž€\ÉreO:iI³fÇ'.6±É³¦‚=ƒ¶°S¨QŽN”ppThÒ� ¨lÊò‰“˜C8)J‰“&Ã~àBÁ ›,”¢dØ"ÂÊ�+—$âÒÝûÖE(M»ø —/]!ü¸ñ)! æ+ Ù)�Àœ¹s“) $zMº¬‚(ž;K Bqê×°_¯ÅuìÛ�‘À4‰Øܱ£¸1¹îeÃŽ"ºâÛâ{¡�0<Ð.^èÔ{øX7”ì{6¡&‘Û…Éà•WàysìÐRHË/­€³lÖ¶�ëo<[ öûé·F%`_€�¹vU†€·E¤àw&ÁÅW Pd¨a† ^d]éQ'ÂvŒ!!Åt!†E‚o�‘—™p! ˜ ‰)§Y~™…6ZdjÀhõÁ¶Zk"èAÿ‰›2D‚¨)™Ù`%W !Ha–~yà\Š>ÊÇc“r!ù,''ÿH‰xXèa„†B|à@¢ Å‚ŽØÈ±£Ç�8”¸(0ÄÇ“(Q ø b “)cÊäøA Æ™8S† ‘³gJ7} @D¡C –€‰4&Q’$BE±"I‹$@|�êAÀG†IŒ$ÙòªÙ³K”ðÀ­[²<¬]øÁê[´QÎ…‚âØ»Jhå aˆ|CHäÀ¸±â #nkX²å˘!R¸4³gÌ,¡þl9@Ý·%HpPì9Àbe-ƾ«p൛m¿å —ᇿº/f\¸¬Èà"T�öP³âÆÐ¡o]®¹$éë–swÆ~=´lÔ¹GX�šÃÖëA'8Û¬µðá£^K>øîÞ �Û¿X".qĦ‰e_jþ^D}ù| 0ÆÚe”(ÞuÚ8!XëqÆAd×ÞEÉ­Æ]_°íÇ tbÕ†V@!ù,''AýH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç� CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦M’!ù,''ÿH°‰xXèa„†B|à@¢Á‹6Á±£Ç� p°ˆ@‰ SªT)àƒˆ�"P®œI³ã�kê\‚ÄΟ+äJ4ˆ-(SéÌ$3†ª4"‰’XI€ø0Õƒ� %VÄzô%Ù³h/–(á�ƒÙ´p/Šøà�íÂ$ÞÆ%+‚D׺† XBoZ%¶þe`q`Á!$r˜L9r…�!r8ê8³çÏ�7< ºôg—%ûv68^ÃX rˆ\:€ä¼XEÀF»v¯ÀµmwûæËðݨóÒ]hâàÂÉûÎ~ÜtdÊØ±s]ÍpsLÖà3‹jþÍ=|fÔE€(>ásà ^"äñáÇ_ËŸ¿þÑl��Ü^"×ÜH’UÂrÆ1äÚXÄ cå]GXü�0mžy7¡yà�g{ †…ßo`V¢snFÂtíQôßYbWQoi!ù,''ÿHÀ. YˆdI†H¸ð¡ Å‚.Ùȱ£G�OqQ “'SªLÁE‰�%œ¬œI“ã›ÔÜISÁž@WдèF'J80é@ P2UùÄéÅNˆNEâ¤bI‹¸P°0ÂÇ& %† Yòå×·p-’€‚ÄCÜ»m]4�Ò¤¯ ¯xã†ÐÛ—oßÃS¤„€x ±SY²å&SHôÀ¹óXQ.[–"�°èÓ¨O“vl:µkËH\–Œäuê(.Bà%ñÁÃØÚ¨£lþàÖbñÀPH40·îòåöö�Íøù׃‡IÀvá¡úò½\ަœZ³”Îè=+¨¬º´í÷¢W „"þ{ÙÆ°·oÙE¼PøVŸkùç]AǽU 6Èàntýg^ц„ÊMx .§�d¸…�ás$Ðf­Y¦g‹9Ø`œ­‡Új)ò÷š]Ž h#j ŒH R„¶£dSx�àE%„ YïEäq¾ˆ^‹>~!ù,''ÿHD$H$tXØB†Œ ™Á‹6„À±£Ç�H˜£@4€\Ére$$’ Ñ²¦ÍŽ�Lr³§M#| m €çУi !éP¥CQé”åMŽ a´jLŠ à‰‘…@&Y(qDI±$Jˆ�Kw.‡BœÈ­Ë.’7!ÞF`oߺ%Ð ˜1c7S„0Ü·DHÜ8 @óæÍn0@⤴i¢%|Þ<@‰Å«cË–Ý:*ìÙ¸? �i21 !¹gK@"€²]²¨�Ë–0ÚI€˜&ãŽÊaºÀ»y�[Gì¤ñ›Ýo·Ïª=ȘÃf!4œLO‚,ƒÏ�g‹žbº¾i$ <Çný:¸ÿÕµ Â{ÿùÇF$¸ _�›Ñà‚vYV�¸ 1š á]$_$„àá‡Bˆv.ˆ7] ÝöÝÕ™(H(gžcà ТxãfÜö™h¥Iâ‡.”–m®ýÅ`�N4à’¹1p#AL¡”޹átt‘ À (—›…Îq)‘õ 9å\!ù,''ÿH�È'OˆÄX#E†1’<‰0‚Á‹6LÁ±£Ç�õ‰à£@<ô€\Ére�'y´œI³£˜öÔÜI3Éž@[æ J´#�DŠ=:ÐC•JYêáaÑä:£¦ˆÁc„ɯ#�n@˜4Ü��mØÜ(ˆš@ÂøΚa62ºþh7w6»VÍú·Ô² äÁ{ÿù—tèW f8�F“5�ßo|€æxE—tèa‡bt�áIGw~ygu%~‚É™·Xp °^ Àˆ´qö™h�}è¡¢åÛjä-ø_ ¨¤mwØHPFœöäb}4ñ\\ (`ÄÉÙVas[†7d}AJ W@!ù,''ÿH0„‚$IV(Y¨ÄG†JŽ$‘  Á‹6ôÁ±£Ç�™¬� £À˜€\Ére‚$´œI³#˜RÔÜIóˆž@[æ J´ã�+Š=:PÀ•JY2y`Ñ$’:£úPò@�ɯ Œ$9²0È %")ùõÃ×·páz¡dIÜ»p? 2bˆ_A𠀄ˆ_¾‡‚dÄà�#" ’Ø/Ê•+�Á’Ï ‰p.’¹²�†K«^½ú4äÔ¬cgV‚À­U"Jd³.‚‰`A–ˆÎ½ºHç%AlcT>x„‡ÇæÖ…} â!´Sd¯_••œY¢ú‡à2_fÍYè÷ @À¬Ú5lÝø»8"}~üµ™ôAôýW6Âòé¦Dg”wsy�`á…þFW‚Û f]_Ø ñ\‡&y€qà%Æ#Rç'VÀ}•qöYc^Ágóµ&�wægdþÉ^‹y iF&Ä ˆ„'ÛƒÈEÙœ…;‚†#’o!ù,''ÿH0RäX˜#N†9”@Àƒ$Á‹6ŒÃ±£Ç�Ó¤Àã£À+Ò€\Ére ‚XѲ¦ÍŽi „s³§M%H| m €çУW‘éP¥](Qé”ešMYa´jŽHLŠEÒ‚’…y@ÂY(ÑHI±!bŠ�KW¬;9îÈ­Ë×dˆ/Wì\ü…ÎÞ¾tA028pãÆê0°s˜/;¾x<ÀfΜ Ì™óåŽéÓ@FË͹ÎRƬcË–íz سsƒÎñ%„IÅ@rèž-ç ƒÊvéÜI-\¶Òwèøö‹¼®)ˆÞÍ[={â;ޝð¬¶ã�/Àƒ¥pÎäåòB(ŸÚóìÑuNë?ýeÎçØ®�€Ûp>V›@vÐW �½ù%Á r„ÝtÙý —i�G]_!Ø!âˆ"VHÐvÀ—à &^Ø©h×Í©÷Xq ÄŸ ÐÈr6ši“‘8¢¦ùGÛRèEXà%è¤nsèxb«MùwLGW Ô1Gsºm�—ð©_‘VÎ!ù,''ÿ=HP@„ `XˆaÆ€AÁ‹6ÜÀ±£Ç�/€ £@ .€\Érå€rhв¦ÍŽ V›@ÐW �½ù…À rFÝtÙý ‡i�G]_8 âˆ"VHÐvÀ—à &Ø©h—Í©÷XqÄßÐÈ™r6ši“‘8"¦ùGÛRèEXà%è¤nèx"«Mù˜LGWPAsºm�—ð©_‘VÎ!ù,''ÿHpÄ’ ºXYh…ʆV¶4¨²dÁ‹6¤Â±£Ç�kt©‚£À9j€\Érå†~ÈѲ¦ÍŽ5Ìr³§Í-K| m €çУsøÐéP¥‘lQé”e�M&Éa´ª•KLŠ]¡Á–…@fY(1II±ÄÊ�;WA-&èê�ëa—&\ »·p’�ÿ&NìEC’&… 6y0ÄËâÀ,_¾ìåÊ•!&B‹NàËæË">ͺuëÔ›¬vM{³–!qµ&ÐRÛ5–!I +Xb‚4ïÖX>›X’cóÈMDhïôëL(ær2v¹Kü¡VpYKÞ¯{ ~esfמ5ˆž/zÈͬaÏîÍ1l�M´×¸™äÁø xYÖ„ öõ¦Åg¤wÑs|5¡á† wWƒß¦`Üi ]ˆ&)0Äqä-ö['b§@+^À~—yÚcnø@h÷½&�x ö—Wl _Œ) �iJ.æ… ˜„WWÛ„ËU �†?ŠÆ#“r!ù,''ÿHÐ�9ZÈX(C‡ †2PähÀÃÁ‹6ÔÁ±£Ç�_´h@£@8^€\Ér¥�!†ÀѲ¦ÍŽ/r¬q³§M<| m €çУqÑéP¥i Pé”å MÆÀa´ª ˆ�Kwî7XÈ­ËîŒ zžÁcoߺvø ˜1c)v00Ü÷3`8 @óæÍ0Nœ˜Á¢´i¢U|ÞœÀ‡Å«cË–Ýz سs¾1C€ÉÄ>nèž­bÆÊvy°@-\¶ŠÑ,xø6÷°íÖÞÍ‹<;b�{ð­fà½.�¿‚l¾áƒÅäò”ŸøÜy¶è¦ó›žqÂsìÖ¯ 'àjµ ÄÀ| Øu&ø—àf>˜ÐF–±Àßp7ŒfyÔMˆ‘ „(bˆ^´� åY÷x�‰—v)Šõà ͩçXq;ÀXÞ;и¸}&Zi’�(¢ ¥õG›kè=8 è¤n'èHÐ)¨6¥c0°0]ì� Íé–at_–wd~EZ9W@!ù,''ÿHPA>dŒY8F̆cZøP“DÁ‹6ñ£Ç�ËÈPc£@iÊ€\ÉråHÓ²¦ÍŽe|ls³§ÍI| m)€çУÓ éP¥�´Pé”e™4MHc´ê˜4ILŠM¢ÄG‹…k@¶Y(@I±#ÄÊ�;‰2 èê�;â 3hŸA°·°€/`ÿU¬8ÌŒ/f 4£áLÆ�\ÆŒ9Œ-gˆ æ3ΘgLŒºµkת'³~M›3™3qM"&Sû5›3_ #AР4o×l@7@�csÃf�H`ïôé £¹ùº\~# ÁLLƒîÓG×ÂYóëÏ3FË}FËæÖ±g÷ÞÏ8¶@3ìñ·n&� Á}b†…™Ñ@}½‘šè]ô_fd¨a†ÂÝÅ w…eØv3H¢IHœqÜxŒýö…‰×!ñ…Š˜  fŸ‰Ù†j š}° ^‚üå5Y€D¾#AHÌpZ’Œ…ÑÀ…~1ƒÇÕ&árTB—¡�£í¸¤\!ù,''ÿHp ‘.]µYØÐ†m u�Cd Á‹6ı£Ç�‚³£À)‹�\ÉråŸ.µœI³c"˜tÔÜI³ž@[æ J´ã"��Š=:pI!•JY&ZdÑd•E:£j³ˆˆÉ¯DRt)´ðH %V)ùÕEˆ¯pãÂ5$DQ‚·ró¶ƒH¢¿`vàÕ+7-û&NLh!ƒõ†2 ¡Å\ÆŒ™� A`ˆ­åó!Θ'�µëׯUÒ¶mΊÀ¸0iX‹¢Û°�y¿>:ÁŽÝ&Ýžmhº@ºv‹[/œ@1¢ÜB¶ËªÝÁ÷¯!ÌŠ´$€,ÞÅqAœ5Ãþ ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/db-green.svg0000664000175000017500000001457415257164523031121 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/db-red.svg0000664000175000017500000001457415257164523030573 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/drag.png0000664000175000017500000000015415257164523030325 0ustar00zuulzuul‰PNG  IHDR ´©Gž3IDAT(Ïc` ())IñM¬Âÿx5 )”À«]YýIΠɃ$É‘B‹B*U3ç_IEND®B`‚././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/favicon.ico0000664000175000017500000002367615257164523031041 0ustar00zuulzuul00 ¨%6 0Þ%°'(0` $CíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™CíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™Cí™CíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿCíÿÿÿÿÿÿÿÀ€ÿÿðÿÿøÿÿøÿÿøÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿÿøÿÿøÿÿøÿÿð€Àÿÿÿÿÿÿ( @€Cí?ÿÿüÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ?ÿÿüÀ€ÿÿ€ÿÿ€ÿÿÿÿÿÿÿÿÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿÿÿÿÿÿÿÿÿ€ÿÿ€€À( @Cíþÿÿÿÿÿÿððððððÿÿÿÿÿÿþ€ðÿÿððððÿÿð€././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-gray.gif0000664000175000017500000001422315257164523032140 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö5ÉûñPÅ…ç$-ò܉15ˆÕ[…©€â-¸pÍ@ £Þ"€ÝÏ[²÷ô™Ë¹Ö޹• Ü­;§«4 Sçò}ã�ßÞ¯ñ{,ÝÁï I㨸ÌnWâ 7À›ÁT�‚rè"$¸Y8Íåò4Ó¬‹ vì)©¥5¸­Ä·Ý¼¥HF–5ø½Ò¤Ÿ8mÄò&r£…{n¤\4êP†[†�ˆ´âÝ™HÓüE4åá£øF†~ù‡ëé«X?öü||,Ñ2�þþ”¶Å”p|þi�.H�†Œmf�Ìf6Ø€¹0˜2_I†kÅþàØ1j¸ÅzÅL2p‰FÇœ�”Ç$¨¢útŒs3’�R1úyD_Žf¼gL-$ÈC^‘™Çl�1À9‰‰º„ÈŒ1ZZ)F?ŽcŒ”^®�e.ãx¦ ™e®Á¡.Øx Žm²ÁB˜?£V�n@YL•²PxJ{|²‘d.)Ââ'œ…Âqæ-¿Ùh‡ÞBè!’N‡�¹)ÈÅØ£©Œ™’ ˜ºì9ª;â‚I¦«²ª h„À«Å"ç)·Þ‘« óáÒc¯tæY¿gEÁ`Ic<(€D~äò”$Ei¬àNKîÃzÛ”K*UŠÐEÁ<ôÄGDÏq>xʃy)Ø)Œp}óØ[#ÏþËfú QÑ�SÈ…,Ñi …“Ñf0l£èP€üä±0ŸÔð¥M`!IÛQT‰­>1éî�Oîc­DðhWkbUR° ½ÃÇTùÑÉ#˜S‘5¡()&'ž[45–ýàÀÚÇ+zdƒ/¨'b±QÐ}"A³ñHç=6èÆ$À‘�§ìG]‡0×sDÕ#ΓéˆhÌò#ƒ‹$fÝ(¨®ÉäÇr‡Ðþ[~41 ’…Âa‘{�ѪµbI@%?b׎õ Ýk;žjŽíÁ½ñïüzt-§åÆÿ– ÔRP ¹Ç°-@¾ ^Ðúô—ò¯T�?ùJCÃØÀè-xÚÚSt –FK‹àáS()æ;¨K .ñàGŒ‘!Ü!¤€�p@6غ%,;¥…‹R®»× ñ(žiä[•I¦¦‰ 8`eÇ“*ü•ÂoãHðÄœ�åÙ †”EAf!ùü•Æ€KÈEæù•øø )䈑–âµâàs„˜`ÙSHx\_FÆðÚ!âS úƧ°ß/œlŒþ0/�\®ãC]£vº„>B p¡iYDxm‚¢üß³JÊÁØ Ÿpç{Ø:vŠk"à×<¾Ç©Iqi$˜™XeG‹�qP&€QÀõ«©}�Q: ”ÅÇŸ•,†m#Ø\ß’MqânyÝ]Pô1Ü ±ÖÜÃÈï0@ uŠí ôûà@¾p\£Ò¦(²XÈ=° `£žn“½õ넨±×'•­¦¼Ýç&¬à¾6 @i5õÖ 3¡å›!Àn¥–K× 0Ç$`p/¼?:Çt¢ ½è?W¸�€Ý}+ÝèPGúѧuªþK½êX¿úÂùMs6ÐæãH€xuí æ`›å�'7Ïþ†º| em–ÚI‘q!¼yîÁ´CÈëÝ– ï¥æ´¿Ì:äâÀU>•ÝÐhÀ¯|ò&¼Ùç°qQD{Ý¥8Ø$_ŠPžó·œÉ7¢ÛÅÉ�Ój�5èñ>›Ûx nŸêã9i;¯^í]/ƒÍ§`ß’æöôõñíã™{2èõíL;Çß`nµƒuöÃHœkGà$KÍ�g˜£Îâ‹aÎ(nâwúáßöÜ_æô˰Aėبs¹‡¿ýô/Óûaˆä)ÐÜ÷‘æ˜ ÐÇMçgWöLëG&õYNSq�þ}çT˜JøÝE½ä{k€Ô4€xK¨>(×ÕWÈMØ�1�^0z¥ð … ͆¸Lˆ‚ ñ�aÐ{§pyøv .È|ôçU6H*ØE·€‹÷è3L58„Ò¹ qP ƒ`g%HM'…ûP„#2‚Kðlc×MxKOÈ…7õx…N� ¬…Y¸L[ˆ†ñà…ZEî׃~ge˜JgH‡ñ€ƒ_PX°„l‡N_ˆ]è¤,8†8|/&|ŒX‡n°^lاð†Ô·z¡„l—(O‚œˆ�hðyñ4Ci7ŠÒP‰l� ú^Y <9i‰˜�sq«“¡ $vvÖIà“pyP Ç•Sð¦£Pqy`h)0¤Kù19J™J ŒSàm*_ ¦ñ©âˆ�ÿª¢:ª¡º� �mŸVñHª¬š�ãˆªŽ™%âþéžùG*D�P¢z«=j¥u°j™ê«ã¹¨z0¦£@–Ä:Ñaú›Lº¬Vp|ØÈ* ÀI­ZЬ½*àËÉ­Uà­°°£¦§äJ‹Åðzµ2¬ëÚ†: Ó꽯Q –É 0p¯øjrþz`®ÿÊWð:GŠ|‹15 î ×I 꺰à­P¦×p'Å0«Ëz Ê ú: ©V°-‚_çZ+„ôð°Ì`±#K®k«.u4)‹®Z5³¬¶°1˱±�­£€±äŠ ,ËF¸ «ÜŠ 'ÊVÇÀ³…â—)g Ë,à‡ „' Y‹¤P¯S² þ‰ /û´Æ0±|«¢€¨ìS 4Z( û=‚ žØ&Ñ€—ú#£ i» i¬*2¯<ÊS[ f $»�á! b{Ò0®±®ª"XÛ·¬á³¦ð¬Ýñ¸¥ ¬÷Á¸¥°kñA¶¥ µÆa¹�KµŠ” ÒÜp¸|q R&¥+Œ¼¡¹—Ë'y $9‘À «Ð(�·º ¡ñº§ðµN"»¦`ÄÛú'  ¸Nâ}: Õp[ž{„Ôb…ͺ˜à' 'À´@Ò k $¾~ÐÃpÉ«)`¼·pë+Ï€Ú;)Õ› W† à»\‹¯ÝË oQ‹¶ÛåÑ‹à¼mÀ÷Ë%)k¤ÿk €ñ+´`­i’ÿz  °$ ÀWà$À¿çPæ{«À·ò`P –0p�P°1k8! ;°ñ”*‘  Ъ0J|;Ì Áœ¢+š·ÅNà¾.9)lÅCàü“ p·\Œ, ˆ0´c¼ÚàŠ(œÆ}b³ÜW {êÆp0·—#ÅtL Ph±L Æ{, Ðħ0 0  ÇƒüÈ�É’<É”\É–|ɘœÉš¼ÉV;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-gray.svg0000664000175000017500000000601515257164523032172 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-green.svg0000664000175000017500000000601515257164523032330 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-red.svg0000664000175000017500000000601515257164523032002 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-gray.gif0000664000175000017500000001273415257164523031631 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö5É«ñPÅ…ç$-òÜ 15XÕ[ƒ©€â€p�€£Þ€ÝÏ›²÷ô™Ë¹Ö޹• Ü­;§«´Sçò}ãÍß Þ¯ñ{ÌÝÁï I㨸 n W¢À7À›ÁT�f�rè"¸98Íåò4Ó¬‹ vì)©¥%¸­„·Ý¼¥HF–5ø½Ò¤Ÿ8íÂò&r£…{n¤\4êP†[†�ˆ´âÝ™HÓü¼C4åá£øF†~y…ëé«X?öü||,Ñ"�þþ”¶Åhp|þi�.H�†Œmf�Ìf6Ø€¹0˜2_I†kÅþàØ1j¸ÅzÅ02p‰FÇl�Ç$¨¢útŒs3’�R1úyD_Žf¼gL-$ÈC^‘™ÇH�1À9‰‰º„ÈŒ1ZZ)Æ?ŽcŒ”^®�e.ãx¦ ™e®Á¡.Øx Žm²aB˜?£V�n@YL•²PxJ{|²‘d.)Ââ'œ…Âqæ-¿Ùh‡ÞBè!’N‡�¹)ˆÅØ£©Œ™’ ˜ºì9ª;â‚I¦«²ª h„À«Å"ç)·Þ‘« óáÒc¯t‹äZ&yLR’‰QÓä‰DYõñ K\£ù>EšÁrªü�+_ÆûÍ’–�ì$.�ÅÆ[V�”—|ß ð/\ÓFÇœ‚IQ+øÒzle4ÏP©R¸. æ™fñj¹Em–Á_§¨^L€9pr”Ï„&7Ÿ@Är3�*Læô6ŠF ð`&á© yJ�œ”C.`´Pò‰þŒB*KAäþo�ε¦:64›õ*�Õ Öz0D9õ!|@hO»Ú×Îö¶»ýíp�»ÜçN÷ºÛ�í%8yjIqì)ü\ÑC'ÙÎßYQd<�I?BàƒV8`Ý _.à_²½‹Z�' åq6ùQŒø AnºG7oñ60½‡·B± >mÒoL°#w²˜ííú�ÚÿÂZ!¾»Ú»ìö¢HâàûÒ?ñÄŽ-þï×pzRH{ …€Ÿ#ª|s ŽømËbðq~ ÃÚ¿øÇOþò›ÿüèO¿úÕO°2{§pcàz¶¿€õš‡�Èß×È{ñ_„ ®IûÛÇsöâ~Š—V 6 ×$ídš‡þiÊÆL pI§ ©×Š� -7z™'€è2xû7{’fMg !�—göâtkva€u{„yÕ× *¸�ý† Ÿ'"{8ƒ„‚<øä.h>ûÃU>!±UoÓèk ˜„?¸„RÆuð|MÄr­'…hfø§ }Gú„#Ä…‰!PD–r¦@€Ç‡ƒ½g†Ý w5r5÷— ܦjH†c‡03_Öumh)êćC3ˆ%hˆ„�é£ l{†V‡‹ˆ.Š8 86jº0n{8‰æ"¡€[Xg²Ç‰ú¹GVƒQðn¤XŠQŠH…2øŠûþ w[ðh—;8i_À¶H=¨‡pÐ|¥À{V tÁ¸’èB|à7ÿµŒñ 9_`Œ–Ø䵂bW'ÀX ‹»¸�N˜%Xèx…âh&º€C�0„€˜ŽÍ¨�gà`eÇ,Þè½H Ó�`°V�€�ŽÀŠü(÷øùèyþøŽ`…D¸�^Ð�°ð‰¦€ŒX' ¥6ð—‘U Ù ’L}¢°�™Ð%i’HÀdIMé’O@k´¨-I“ÐN£Ð‘šð„¤€‘.Ù�@iØp'ÅPŽâx w () ø’-‚jU:9Çà> D•üH”þèÇP„âh‘É&–\b’`©”±��¤`”‰ [ÉS—Çðˆ-u lY(Ùw!L¹�ʼn&–“š²‘zciÙ-Èà•Èt BÉ'î( —Ø]Æ ‰…rŠ¤Ð”{… Ø&Ñ0µe è$Ñ`™›‘X ÈÖhƒ`˜¥0™9”�ÆeÆð˜Qf dyIŒéa˜¼ny Ù›Ò ’ñ±›¥à}陥0’Æá›¿çȚˑÜ`›|‘‡R&Ó v¼�œ¥`œ@yÇÀzS ܰ �2¨Y a˜Ýy Œé$ài 0ŸípmÒ`¹i% �ÓP AÌy é)Çþ àœ˜ U °—@2 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.509496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-green.svg0000664000175000017500000000472015257164523032015 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-red.svg0000664000175000017500000000471715257164523031475 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gray.gif0000664000175000017500000001241415257164523032467 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö ,P0㺀$<|, Ikú(ôÃãþ—¾â!˜(þ‘æJâ´�‚ÃC $,‡`h¡�å 0X‰i€Rs œzGÁAµ€n‹ u�»m¼ÖÃGnfY:ä„$÷휥Ð$8mk÷ 7·Nñ>ë»F0±€Ë>0q¶ØEÂehÛ ¹6mz+ƒ�êLšæ# ´AøÌE2´Ç¤� àì uj*¡Ÿe}�d€¶Ù´£4F5÷ºÏšú†�Áð&l�};näÂ`æOvãB�ȳÞÕ™ò'(­Þ! u~¬jK“µ¾ñª(©ø1iª½Îñ«(~d9J±x”: ¡t¬Ç*³¨â²œÃÞ"&µÕz‰±ÜÖÁ¦-nÎáÜ´þáڊ˷릫¸p\º¬»zèZʵoˆx ¯ôŠÚ®ðö[?rK«À{( Àÿ"œG’p€Ã²6Ì.ÐRìï®nèk‹Æß‚ï ð È{à²m£|‡¼¢¸8Ƹ¥dìòž«�K:7¿;]Ù–âiψÌï À ÊÈDlË hà’fÓõîk££ÈLõÆ™ÑéÖ|”¬õ¸Œ v·Øœ…³¡œ|¶Ï—ˆîÛw`ÊÑXxÜ'Ý)Û2ZpÞÉ·ºqƒÑòà®ÞR®ë‡8Ü{{¡ôÄ�ë!Á-¸rq8”s@ TdK øsK¬Wt9Šà}`ŽQ úþ0 éí“âz9°C!ûìÄ€`�Ý ˜Åæ{4ðzìÀÓÁÕ¤ÍEФ8ªÇîäôþÄïͳÀç¿ ?E¢¦ ¢<ïÌwØ;¯ä“BöÜhï÷êÓ�/4ËOöí€8_öÒ—?g€ïVÓBA¿mد ø+ 1ˆ ñAÁ}È"„ëG@ £t®�Vù®`Áë-Ïw|Fíh ãi‡\…Ø ;˜Âa€…µXœæˆjã�Lˆ` …Á>L°í`V å4xÂí ÑûÃDÿÞW8‡Ðâ„øÄ[¯‚…«Âé !ÃÒ°‹%üþ˜æm]±‰÷Cc1þ´8ˆøà 4!(GB‰C‹‚ê2X,>ƒŽIà¢3,p4ò‘Žt¤\"©1RáˆKd"úPHè-¡Pä /‘F$äî�›tb'Ÿ0IxdpÊrá¸î1ˆåð¤#ò°�ab] é D"A”Þ‹BàÂC(qeOh¡%ʘÅ3>C—NØ@èÌAÁBLQj;±.AÌ9ZÓØt5¡‘ÎBå± "¤¢-SÇUFa0‡`ùN&,ˆœ·Üb.§°Ìr¬Ð— ã`†º@¬³˜ç,F;›ðsô6Q(0Ká6T�“ܘh,º(;í°xóÃCÍþ Òmˆô…ä¸èÍZ“�U2‡ñÒ%¼£™†0X G¹C¬”Æ<ÂN…ÑS%˜ã•—0X8‰@=B¢œH�h2¥°’b¢d‚Ô¨&?ªÊ�N�—åØçWû™âa«ÃHª–Š‹¦at­«+0…ŒŽ�Œ9Mä@£PQsØõñ”'ì¸0€Ò“�ö„Â6;S±*AY uh`�9Ø'Àu‚®PâñõÖÍ*µ³Mh@>ßñŠA†B–>Í!&ŽW­òô èÜ;ÔŠ ˆl–²u,YëiÖ%l ‘ù(bU~[ÓRLͨ¦�+j�ð�Õæ®-p�[ZÊ ´� Šê/‘ þ·Q@s¬l)xkñEWd7.‚-Ϋ»è!¯rì…Á¬W„ª†‚¿ ­0äzßõÞB¦âµ€­ƒÒî².E7\‹ ¡¼¢H©JíKü>Á.ÅM‘0_§·¬P]a°áx.`½«Ó�B¹Ø/‰ƒk‰Ïâ‚À¶q(nø ƒÅ0¼„€l[4*㛡`ãraâQ¢8ÂÕx¯”�³©òaÉ·ò<,Ak@‰NPX•ͨÞ>¹gÎd‹æ?—ëÍ@Â�ÁÆ>!±~¾*‡Çlàu:ÐM賜çyçï¢1Šëà+²»æj¶Y‚¤û‡¡0Nþ%ZdÖÇ„ý¡´¿m×=þ4B= D’°~µ¬qbÉSgÍÓ4ldsgµô¦ªY-ŠÃö%Î.•§9ìé¾FÑØòþ™ê\sv×´9t�� m\7ºÆØ~ͦÃZÔB6;Ü©)¥¤ ]kŸÝŸy¯—•]‹#ãÁÇan²^}c°Ì:µ°·Àl 1ƒÚÙ¤�v°cKþß×÷-ŒýLºX §dô‰‰ëÒá€6lõÄkaðU#ü3êVMÈû@p’KÜ×Jœ)€G{tãîxnΦš×‚æ·2ǵó½Ø"àVøøüÎ]\ÚÜ¢WÐö¢Èt�þ¿Æ`y[ùF^Š’;EÇ ´³@Sͺ[ºð†ŒÒü½Ñr÷°êDÏ�± t=ž½Àiï Ö³gsß=Èy·KBÝ.FŽ ¢å]y-Â~"ËDë÷†;;�^b-ŒÛìßÖuÓ?#Ú-ø÷¿õý;¡_–”ÿšð_–ü5÷³ì-ÔýáªGgj”(í(ÐyÛ,�½DSsy-| ¿ž¬cMüHR¡øÈ—õŠ+cú¶ÿµrÝ*…Óyè³að÷úÂ{}m}´UÿñŽï~nÑÑ.ˆMü\{~š�~Ã}?>ïo?°O �GA‰¦–ÿØR[\\ÿ`ðLe'l€>×`hBuþ“S€ HvÌÈ�dp Cu�~c�a %ó Rv¨@г0fU#¸6¸‚_�3)(�I¶ó‚Y Dðå.Hƒð×zdÂ:а¥3%øƒýÇcn„FR7u#=Kè|¤0ocЄQˆ?“~ q… GZѲ‚\ˆ%ã„~რ(1@W6˜l/0Þ‡€g��rp‚¢|mh†eÀsìv…·6n˜‚ô7…}ð{(HƒrH†v�ˆth�s„7Hƒ�u€I¸��¨vxˆ ¸‰{0Â� 8†�è‰È†ˆ3+Šè� Ó×}cØŠ~`ƒþ`aƒ3‹®0‹µÇ7¬Ø‹~^Ý7ˆZ‚CpØ3¶HŠËŠ`s{ž¸(º}¬H‹‡ 1™6“eÖ`�“H7³x(E4ò6Ä‹°`‹·x6#iÀ‹ôB Œ¯ð�î.ߨ�/ÒŽ=s�ögŒ"ˆ0ü˜¯ áÈ,Å �ÑÓ#S Ô0ÅÐ�zâG´F åÇ,ïe "‘µ2� ŠÿÐI õhõQ+˜'¤á ,h$Ü y A²)ÎP�ZÑ�¢’æÁjBŒáoî±%- TH×a$ÛèøŒýÁ‘ˆI §L ʈ”а’¤q’Åh!Sy�Ù¦ þ?”1s’X µô#eY=´‘”£P•7bˆ·À}eñÚPI àŒPøiÙ(•–¤0NÑ— �•1rhq—ú¥•¶€½Ò„ÂÀŠi À˜µPù–Ùˆ"™ù²Ð•j.˜—9’À æ0™€b™·p––0z“Ãqá…™r3p�ßašiP™ú šÅ2¬yà›^ð™ÁI/PœÎÀÈ)À±I Йé2~¥€ ™“`à$@Ð ¼É 2a˜.Cí$AP  €ŸП$!A À3íšBÆ 4‰8¤Y[ îÁ�oóЩ ŸòÇ“ª8Íi�ˆY¡Å ˜óYCÒ0„aø-!GRq¡#Ú•¹™û š¢Z�ð¡Ï‚�€¢0š£:º£<Ú£>ú£@¤B:¤c;././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gray.svg0000664000175000017500000000415115257164523032520 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000021300000000000010211 xustar00111 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-green.svg 28 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-green.sv0000664000175000017500000000415115257164523032507 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-red.svg0000664000175000017500000000415115257164523032330 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-gray.gif0000664000175000017500000001343015257164523031414 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö5ÉóñPÅ…ç$-òÜ 15 XÕ[„©€â pM€ £Þ€ÝÏÛ²÷ô™Ë¹Ö޹• Ü­;§«´ Sçò}ã�ßÞ¯ñ{ìÝÁï I㨸 nW¢à7À›ÁT�ö�rè"$¸I8Íåò4Ó¬‹ vì)©¥!¸­D·Ý¼¥HF–5ø½Ò¤Ÿ8mÄò&r£…{n¤\4êP†[†�ˆ´âÝ™HÓüE4åá£øF†~¹‡ëé«X?öü||,Ñ*�þþ”¶Å˜p|þi�.H�†Œmf�Ìf6Ø€¹0˜2_I†kÅþàØ1j¸ÅzÅ82p‰FÇœ�”Ç$¨¢útŒs3’�R1úyD_Žf¼gL-$ÈC^‘™†Çd�1À9‰‰º„ÈŒ1ZZ)?ŽcŒ”^®�e.ãx¦ ™e®Á¡.Øx Žm²ÁB˜?£V�n@YL•²PxJ{|²‘d.)Ââ'œ…Âqæ-¿Ùh‡ÞBè!’N‡�¹)HÅØ£©Œ™’ ˜ºì9ª;â‚I¦«²ª h„À«Å"ç)·Þ‘« óáÒc¯t)   t˜  »Øð{A è ABùÒÑ1,(A@TB!'‘²,ÃÑ*¯ã„Ì�‚¬ÜÌÎÒc…Áè @ Ô\Ì!´RŒâÚt3 - 6å"ª£O“ÀÀRs³ÃÁ¨éj-kb×]{óu0kÂaíØ¿$ AÚò¬-‹v˺¡‹‰²X@÷@`Ã’¥?à KÑtA,x—ò,ˆá’¡& $®ÎLãB§YbÍ–k„AÒ˜¼l Å`,:¨&!„Þu˜|;Êþ´g¼›É®§½8¦º¤�ƶ¸Ðjâ¹wÝÀ!–}¦“¯ )—\|Ú%±ô¹¬V{.ª’Àô¡>ϸШ�R9ø¡3p½¹È8Ƭ†D;ëT"®(ÃÒ˜>!Ä»_èŽW+\ðM æÅäþ�2²Ïz�èœ&Fˆ¹9pz€àÐw‹l]¡p�„ô.X< "{bPÖ(õ‡�ð~„·0Ã=´î…÷ƒÝP®†·�B@�ø(¾>Ôð Í 8Dj�<ÁwºpD< ­‰ìË`Xpá¢.¼É(D±È¾bM]H¢(�;2^ÐŒ}ààºþ¨ˆF?¬Ï�$�wÁàÝbIzÀ#ß×,Ãa�S0óƒyA-ò!7!Ô"óÆ¿1ò…˜ÛƒÃ y…*zì’8„.<øåñaŒ  YÇ+�/’{¸c*I¨Ç<¨‹Zð¤®8Kn`�·è_xÈôrˆ„œ1;‰ ÔÅa„Çô%¸Æ+È‘’yb4_XD;¨›ÿie ¹MVoâœB.�um–“„ݬC……yñ�—ïà/õ0IR”Æ é´ƒ1ó9H=Tª‹‚y8iû”„ù‹Wüª@Íðá¡CDg@� F‹êÁ’¥¥7Š)FÁ€@Ì*CþÊ>â¡¢Î<.p5|áqvÊ€<ŸÍÃ5KѽzÞ"¦m�%ušLô”Œµ¬Ã7Ï5Ù�²‰ H¦àNºÔW]Â-«ÉJLZA©NÌIÉš†Ò¡«÷ûªV >­º!а|Båw�pc[ðë[®°*Ar{ k�ÞÏ„x*)XØ„òi”ˆ]H\§—‡ƒ’Â|â¹EMê@»Za ÷3­™rñ)¸u‚e_<»àØée²² º”àGpÖ¡�àÛ]ÈTë„á ¸Ó[eöçÑ'|—zªåY†Eïœw`n<ËV°‚�°eˆmñ¨[þ‡ŠF¯¾ý$Õ`ÝÜåAb¯EB?•XÖâ1– µu�P {qñµù �BƾûRŠ¿F0 nX¼Ý©¹®Ó¡z “†öû-ø ŒßàâṤ g›N�¸L."rñ¼ ›¢¨uPlÚ”›çŽÆrñ(„¹\¤ô6v�\k<½!{ëMa)6gÐå.¢kp¨ë k‹©˜it§'á50Øuä­V.‰y™b´ò´­Ä«ß;Pøh>lëëºÛª­–ÃMAæ#¤`”zh¯åÀ«6OwŸc)oc:8n�rè¨\‡=—"ÎHP2)=þ[Çgrè\‡MÓ¨§X <�8J«AÆ]#u±�\Ù[d˜°îšÞŠŒ‡©†Õ¦8àX�8sµ¹“õÞlŠ/!‰Â¶ƒ¡-§l3ûo„žƒ¯éû±¥W›Íód‹S'Ò +ˆ/ÙÀgç2àYÜwkw™ ïËr6 ÷³%‚‹Po:ç.Ûb˜¶€ñ@›o#�ÙÐÍ㋇AO:*œ÷¬ª‡k'®Ú_°øß05jØãʃÀÛG†w‡àN»Å]\ ÓÙuÌOÜkî¹öôò÷ôf‹…p»·Ü¦\ôêô°pðeõØ>²¾q!b%D܆$Ä™±� 3þv¹òn.à{îv¾�€SØr>µîÛç¶½pÈá&8ßkW÷ˆO­QV #/c¬¬[�uy¿ ÐTHú…(�CnW^ëS,Ëã¾i�ž‚à§‚Ô$OÁd¡7DÕMf) ]]^DQ¡£{x»èsÇó8h— ¯ìºwWÿ@�'\ ¹åÃái:(ÛÁܦð@q�iõòþçïµù0q!ü=H÷ø]îMo1—?áõzH:ôkÖúußÂÙQàºìïàðí×,̸=»4}ûlšÿo݇Cì îyS€6ôï—éí FÆŸTýn0sùGa‡o¸�sW€}Î7€±þOz�{Y|!·ÚÇ€çp€^ôr¥p e�ŠLy5|r§L È¢Äh[qZ—z'8m7Ü%qUÐ[göÏ÷‚Èà€ö6~$8‚y €: È9|Ä È&7„ºÐn‘Åb]0_j×dÇ„¤jwÀ‚^`ƒKvg…·`dq„ø€rP…C(†ch ·†Þ¶�vÀå†ã„y �¥~XÀwœöQtx 1XHdè f(K8„‰·†¥Ð|_À\Ö·w‚¾'‚>HC…(E7„ûwQ—È�sh�èE´ö’5 Šù§E¸k%X†‰V™8€›þÈ3�÷,è}°{ï7‰g‹„hY T‡H{vöŠi`i¤�PbÐQ¾ØBÈ{Ìè.¸ ‡_ ¹à�~@9ˆQ™�‹ytdŒ°€�Œ¤�2” »uø‰™°Š³¤øóg ¾ƒ�ü ÃÅŠ�d\‚“  ·/‰’°�äDB%Ð’��u-‹–Ò @‰CC¹%$9FY1_>(Ó “)§rã`‘X3H#þ{ƒuº@YA3ó77“3âð†phŽÄ @2 q2D©ºÀˆt ‘ö40èp0„‡eã}o)  p#0c0óƒg‡±m£�sà�¤€€õ’f�xxh �¸™lP v‘Ó™¢éŸI Ñ&Qƒ™šgЯ)UÅ`™°i9 MñF¹‘·©¹]y”¿9?ºt6›Çi‰¨ ˜‰ŠÍ9̶ù”©›Ó k5…ƒ�¢Ð†Û™€ÌÙi�㉄åél šé™,( MwBëùžI°š¤°—ûX˜ö9§H © 0À-ý¹éVŸ‚0þÕiœzÝé�²œ4Ø CÐQ£0Ÿš�›¤šý¹  ™Íp'ÅÀ‘·y ;É ÿI }–ž-‚'ZšeÆ  º¢Ç顾Çp�°ÙžsÄ1\òž:J¢²ž£¢¿‰ 5Ê-ڜȀ•5AKŠ'b¢©Éw¨0Ò-ñ©�ŠQä‚ 8j#ÈÀ¡…Rœ¢Ðš\Š"½"¡ 4 Èà�e »!Ô0*Ñ@–§!…ƒx$øY‰¬¡¥¥À¦9¢¡¦P’¼! hêÒࣷqž>Ù _:©Ø�¤¦p�ÝQ©*"’Z ŠjZ úùœJ‘ÆÑ›‚Z .À �:= eþ¢ª7©ªÁ'\x }8‘À «Ð(৺ ¨A«§P¦Nò©§ ÈÚ›' ©V¢§9 Õà0ªåÈ,âÇ #Pªš / '`¤NÂj $`®€àÃpͪ) ¬\¯‚ð 讣¢­¹ðc±#¬ 2žãÊ oQÆZ |ú›P­èàÒÚ'ü:¤ :ýP`¯b@ Ú  �éI °$ð°X $°çìšš¨ó  –0@�`p:j8a²ö© ‰S*Q  €ªàN{?Ëà±1z§ºz»ZµÂÀ€iвZ+0°U ûµV³³Mj¶W  ÑIJl»¤$T ¥·m0T#@µx+hÑ5P¶ °µ§à0à·‡;¹”[¹–{¹˜›¹š»¹œÛ¹žû¹5;././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-gray.svg0000664000175000017500000000416315257164523031451 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-green.svg0000664000175000017500000000416315257164523031607 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-red.svg0000664000175000017500000000416315257164523031261 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-gray.gif0000664000175000017500000001605215257164523032001 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Öj¡>ˆ�þ‡�}Ef‚ÐvD6Ù �ù0™BŸI Š{áÜAVj¹ÉŠaB°‰ ��Ø%©¦(Oúu�ƒs’‚VA:zEcž¦¼hP�m(*Db¡Hš y”A:Š ™ú„É�AšZ* Žd�¤ž®‚i>#y§©¥®Â¥>yž­²’C¨¤Z­®@ZP¥òPyN‹¼²’h>iÂã+¬ÅÂrê=ÿÚl,ÇÞCì1ÒN‹ ù*ŒÙt“�;å €_Ì™“ ¨úìŠÑ¹ 4@Á=PàÀ°µ'>ØdË L0Ñ ÐÕ¿ç€GŒÀÿp€(þ9PÀºATŒ¬ç0$±Á3= €Â“2ÌŒøô)�€<*àôl9^ú"¬»ÿ °€ ZA2£øLÈ Ê÷°ª�Ä)DA ,°À@½@¤ÀMlæ#A§´|16 Q� n", ÀúÌð³H3—ãK»rÇÓr>ðö#T� {_Äq9×ÚBô9Þ3€ßç\Ðï%(pï= }Y¸À›O GpÛç�n—É·h ¹“›Óž`> ¨Þ�úK‹ ú$ÌP|Nî °Ìx9W‹Ê:-®góƒÎç°pñ(oŽ,E? Ãþ8gCîíæT±„)(pÏã‘ �ЯÀY%<èÎ`Ž ì—’þ9-è¾éóÄ`1èuî¥b§Û^÷ò!.g °C8Ϙç?ý%�. �HT°Xxïçº�9Z ÁU  t>ŒÆŠ¬uP8À9ò· –Ã4dˆ†Â× }˜éh�‚9PATÀeãhžH2å ù½014·Š˜ã%¼ÈË¿S Y:šöÆqYæPâêðA+U<°�"YaÅr\`Žÿ_9„Še Kä#Gåfq„˜#‹ ‰Û8Xh }`Nc"aQGA.0x…×ðþá0‚.‰¶€9&ÀkÌïÐã8™�8–£Œ¶ˆä2-|¬çñúÇÌAKVØ�9lˆûVh puë.\I^òr…>: �åÀ¸%ónB7q¨ŒNÇŸ‡ÊAÊ[ vâhNòñCQ naŒ‡>)5Ö‚ž×\'ª¤Ε¥1ÉÌçMŒy®]‰ÐÈRx‹H^ d÷¸%(n6ŽdÅc¡åä-J�"6ä�NÚ'@˜9gÚ‚£@Qd|· žCHÁ¨%q�Ä<¢ õ„*ÅÑ�yf4è(N:�u¤Ùª=g¡Mr,õ™æpS'^eþ\D–¸¨dI‘‚ÔIì4Ùe9zYCss$EÜQEÊ�_Žã¬¯�å$-G8Nlò‹úIëÙºr¡+]…·:6¯ >Àn‡I�’Ÿœfb°ðGä Jr5�%Gy–Œã› 0ÄoÓ­AR°D™¡8בR­rP&ê2KŠâ,^V+˜Ýlq+à° vÀ°æ€hš•ˆ0Xƒ"€Hæƒ%´�A&PfÝQ/t‘¾ì‚i+çF|)wŽ/! °‚GÀl‡ ó9²[c§ze‹ã0j$ð�˜âDÊù± àçq”÷,[€�±Hˆ»þ½²+Šæ#"ðQ�¶d >Èχ·À›ø`5!&ûgéb„Âå�׬ï¡l€�Øj„µY 1€4 Ôø¸"Õ½U¤ ¸®’PdPöZ�Ÿ�ùê£�»½ñ»æU!nÁ\ßÄÇøøï‡Å|ù@ÈU�¸ýS¤ÀÚá�ø8P>[� ×ã(!®™··ýäµ!j§¡Êh²ÄÑú(!}yÉç|=—qx7g÷ kwwâuc‚ð€øO@{äÐE‰F8‘|¢� tˆ‡¬ä÷ n#avQvˆÓ'8ßþ´€æ`k-q÷ D>Õ‚ñ{¡RåUm”}!z‚€ƒ<(Z1(AÌH@:·„æpGAáRæ�Wˆphå�w,Az<7_Z8dA�€ç@†Š€ZAQ Å�i(>^X`˜jpÔ†nf†Ø[*eUªu‚H!‡VHÆÇƒˆyHnˆCH(ØÐQBX‡Š•šu3h‚ç�t7‘~÷ k }hZH„�€ 8.gž¨„ø‚,qo8‹÷ÐNA¡jAtŒØ}WØlh%HsÁèÆÖƒWuZAá‹ðçäyþ7�ù {æÑ·„¼È£ˆ}�PâÇoWÖÃ`®ˆŒíw ׊P�‰‘zâ�%¤ñ¨å(õøz ·Ž-ñ�@tÄØ‚S”ôÁ�ˆ°Œ²W�(H—…˜{H±bÉ —úH&i<Ø�A±S•ˆèغX4dBÀƒùQø04X¥Èh�W¢C*°a-8óøØ(™x“äÀ9uÁr§3gW3 縒°ø%¼U oêÇŠ,1�¨+v•±�S“Q)9 á·’Ãh“jZ)ˆ'Á…-ñqé™�IA‡±f‰çRþVP¦wÏeß�1±iˆp‹+>ÿšs¦pq(ñ´«àH§  KÛ ²÷à£_^‰ ù€µüt1€›lK ãéÜÚ àzŒþ ™ZÇ0¢æÐ_+2AD¹Ð;«p‰us@K‘ w{BË Š� N}ØS]»M¥0;ñ¶²à¸-Ô²Ápr�{ Ð{3!¶£p¨€šœ°*vk³÷À¥`®q¯Dª­¥Q  ;™‹ 2ë´ 'z†®€ºÈÛ´.©·jû¨ÐS­°½‘+À»n¤ µ¹Ù·¤0�ˆÊ ú€™Ä9(a¾�#h<¿žk-³€¾€„ìZ `bôê ›K°ÎEšñ¾R´ ¶Ô™ª°àCµ€¾ÃI {{~­À2+˜ ׊­©ðA ºå€„­€À|å@ùz ¶þK¸¾°ªAc¯Ð¼Ã:¼ûÁª›UœŠ³Ÿ­ñ\ E)r �[J© <ª‘”äNàA7\ ký* ¼…Ë[ 2||¬ x ®g�Å �òF ¼»Š?< QL˜ ¿J±ÂÀº9¨Ã¢p¹Cô @¹ÄBœ°Lw´£0¹)ç V;�ñ è{»vƒNBз�Á7 Çš R UÁ°¹é[ •ôk·9fú°ÓûÄ­˜ß E @hº°Àjû¼[õPÄä0²€B´-y�,¤ÓäÁÂ>2>pwÅ� —`ü ®<µx(ÖP`µ| _|ÉÀ`»œ«-¶ã2þ¯9 Í ,ÅÚ�ÂuÆŽÀ‘â–íŠÇÛŒ iœdµÊåL !+°œ Ið5ë< êHÎÙ�×<Ï¥ÌÁœ ’ÜÏúŒ _5⬠ƒLSЕÐÌpÄq+�» ��a› ÑÎâ°‡ý®ú@É,ñ�ë¹Ñ•pPÐÁÐ-Ò‰ÀЈœ1ѹ*]Ù¼_\Å)1Ý,-Ññ0ÌIyÓox& UøàÑ>=áÍØr:],urc]Ô–‰Ã% ΄\õ!Ò‘Òd© ],–,k¬1ÁÅòÏð4$±¢µ; %±Íeí"T]*çìÆáÕå0Ö€rþÐæÀ¾j�_í#Ó÷Í£¬%V�Ø!ÂÓ £ØñÎ1‚Øå³)Öå Ë²ØŒm ‡Œ×7’\1ØÃá½AR*Ÿ½¨üAÙå Ù€ÒÆA®‰ ±Í³Z€Ý©}\í(¬mðÛ-›Á†m)ðEQ^�˜}C<-Ú š­ ÒqK ( 0ÐQÝí /0'AÜÚ²Á}'€ÞÂð(qÝÛÝù`ƒØ€¹�ÕE}ÚͯQ ½]f½ßÌ�ܽBß6MàŒ0=ÁàÞ¢@üÌ«~Àà¡5P þ¤%�ß(¿‘ã}ÓÀݧ� Ö0 °Ï`,mx1â~ ;¾   €ê� °ä'ä;‘þãz ŠT²-å•°ÞU>�âX¾Ðß[.äþå Àâc>Ôf. ‘æ�âmn 0Óp.ÑÅsŽ s‘æ @Q¾ç¾€˜e.èÚNn $�®è–~阞难éœÞéžþé ê¢>ê¾;././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-gray.svg0000664000175000017500000000600115257164523032024 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-green.svg0000664000175000017500000000600115257164523032162 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-red.svg0000664000175000017500000000600115257164523031634 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5104961 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-gray.gif0000664000175000017500000001636315257164523030737 0ustar00zuulzuulGIF89a\\÷õõõúúú÷÷÷øøøôôôüüüûûûþþþýýýùùùìììîîîòòòóóóêêêïïïñòòñññöööðððéêêîïﻼ¼¬¬¬»»»ÕÕÕÛÛÛåååËËËÈÉÉÌÍÍÝÞÞ±±±ÀÀÀÄÅÅÆÆÆ×ØØßààØØØÚÛÛÜÝÝèèèÎÎÎßßßèééøùùâââæææéééàààìííêëëÔÔÔÐÐÐÌÌÌõööòóóÅÅÅÖ××ÙÙÙÁÁÁÓÓÓäääÑÒÒ©©©âãã¡££¡¡¡çè踸¸���íîîÍÍ͹ºº”””ãã㮯¯³´´¨©©ÙÚÚ¸¹¹ííí§§§½¾¾ÆÇÇÖÖÖÁ¾¾¾¹¹¹ðññ³³³ÑÑÑ¿¿¿ª¬¬ªªªëëë°±±———ÕÖÖžžžïððÞÞÞº»»äååµ¶¶áââ···ÄÄÄÈÈÈóôôÒÓÓËÌÌÏÐÐÚÚÚÝÝÝåææôõõ¬­­™››¢¢¢‹‹‹ ¢¢™™™§¨¨‚‚‚ÿÿÿ­­­­®®ÀÁÁ¦¨¨ ¡¡!ÿ NETSCAPE2.0!ÿ XMP DataXMPB?xpacke30AFDCB" xmpMM:InstanceID="xmp.iid:0F7670F9E3cket end="r"?>!ù2,\\ÿH° Áƒ*\Ȱ¡Ã‡#JœH✋/*¨È±ãBn5±C²¤I’‚šPq³Ñ£Ëˆ v$t²¦Í’‚íHð²'B™7IjB´hšA“”ðé3�¤&%A2‡AŸ«X³ö‘QÉÈš�ÜðdZÑ)T¡Tdh]Ëë*�N ò0–,D7gsªmË—ï@'í°ëPÁ“��$è˸o$g›0 ¬ÐCT$�33~ U�Ê|%Ie±æÓ|$1™¤.ePÍAM»o ¨ZÞ�éÚÀ׊Ʃ›é“;‚+o‹ç`ã8÷.Ÿž•wÉç/�£”N½ûV¨s^–ÿ(ˆ»÷î2� *Nööóð¯¦GéZb‚Ãv™�ß}NÉ?­FR ü¨�TPt fIáÁ„T 6˜€BÕÇ�€‚üVa|2”ä�E×}Xas$MæÐWšha\vâÐ�û¹xÞx6„ 6~ø•Œ ù—b� i‡Š ˜‘bˆ B ”4“ j'ˆBnEå‡H-…~nlYáƒ@å�bˆã•içGš"aAR¡<ä©çž|öégŸl÷àˆÁ8em<ð¡è¢Œ6êè£�šœvM„VRp1@ªé¦›â1Am!’d��oÖ&œ¦ªª¢zW’YÿfÇ’µ±ê­œòPÛWsÆJƒ¨±�ë°šÆ@Û }•j)묣x€Š„Ä+jèñì¶‹Þ�Z–³& !j pk.�j&d¥!U#œç­[Ð¥§¡Ô½øæk°ŒÉK½šÙ«ïÀ—Äo_þã»l \ðÃøìIìZ;nÀg±º‹kÇ¡�9|S l•‡I$›Œò*oœ¸Ix¼lf"ÛDàUßÜGÎXÕ|’ÄÌ‘T­¯vt¾:óŒ³IIçkôZÈ$+­4ë[Ë ì±r œ\ÒÕ\{}ïÓZñZ©õ<ÚIk›ÔvPdgõjA ²ÆxÛ·|%ÿd(Æyþkf”T§ºH$®øâŒ7îøã�+2_Qä&¼ÊyЙGbßš �éùyd&n £{×eBœOžzmV.¤äëË9™ BFZE{m¹3´ãî»’T¦B4ޝH±ñ™%ãðA–&ó}Ù ë†(éNýZ¡Ú1tC L¸½pN¢!C ŽŸÕƒÉO$ :oà÷Ý÷ÞöFÈ‘{ù1ìù|ù9_D�§ãp=Ù‰ÎîØ¾ÞotÖ! vzr ;Ì ^öÄ}ÁyˆJÃA {˜Â?;ÈFL·ùÏÉBÒ|ðCªa�í"+”(ÆDœÁÉg@sà æ†üÉaI^$ÃC,åþ£Í_³Á"Ä,�I‹rÞ#œÐʼn�¢\¦RÆpÅ+6 Ë ±˜ e(F!ÊYj¢2r$&3Á[NvâÆž€D$ùJÉJVXG¦d$#|ì£ IHA!ù,#2ÑIŽƒ*\Èp!‚ æ0B±¢Å‹3jÔâ� C†T¡ã@%"Sª¤8Ä$+cŠÌщ̛ —@„�³'F%‡øZÈÀ"D“y販ӧ&™JµªÕ«X³j¥úCêÖ¯`à é*¶¬Y®^ϪýJlŽ8©ú6î\ªMØÛök��i…üx0€ÀYûn5\pUÆ|gÍ €ºz Ü�JYsä²I²†Æ:úóÚÓ‰%£FýƒH××°cËžM»6l"!ù,#2Ô` ‡ƒ*\Èpá‚ rC±¢Å‹3j,â� C†à£ÀcDª\IÑ‹ÉEXÊɡ㖙8An€ø%§OŒc àå§ÑŠ r9Ê4Ì×P£J5)¦‹Õ«X³jÝʵëU1U½ŠK¶ زhÓ~ «¶­Ø³cÍ@ V¹tí^5#°îX¸bÏ dÛE°@†æ Økbă±>þ»˜+ßÐÜ@¯ÕË�)£ Á•ôVӢݪf\yõj1ÀÊžM»¶íÛ¸g;!ù,#2Ô J�ƒ*\Èpa‚ ÖB±¢Å‹3jtb � C†”B £ÀRDª\IQ‹IWXÊY£c•™8A¦€%§OŒR L å§ÑŠXæ8ÊtÈ×P£J5ù¤‰Õ«X³jÝʵëÕ'U½ŠK¶ زhÓ~ «¶­Ø³c­H� V¹tí^µ ïX¸b‰ dÛD°@†æ Økbă±>þ»˜+_¦Üí+A¯ÕË�)£¥Â•ôVӢݪf\yõê'2ÀÊžM»¶íÛ¸gË!ù,#2Ô8@C†ƒ*\Èp¡‚ f¸@±¢Å‹3jÜ¢� C† £À Dª\IƒÉ#XÊ™¡£†™8A:€8!§OŒ  €á§ÑŠ!r8ÊôÂ×P£J5ùÁ‚Õ«X³jÝʵëÕU½ŠKÖزhÓ~ «¶­Ø³c;À V¹tí^í ´îX¸b) dkA°@†æ Økbă±>þ»˜+ßDÜ*@¯ÕË�)£õÀ•ôVӢݪf\yõêÀÊžM»¶íÛ¸gW!ù,#2Ô Fœƒ*\Èpa ‚ ÆQC±¢Å‹3jLb� C†Ä ¡£@Dª\Iq�IHXʧcŒ™8AF�È §OŒ 4°æ§ÑŠln9ÊTÍ×P£J5™F„Õ«X³jÝʵëÕ4U½ŠKVزhÓ~ «¶­Ø³cá І V¹tí^…#´îX¸be d+B°@†æ Økbă±>þ»˜+_ÞÜ:@¯ÕË�)£uÕôVӢݪf\yõê4YÀÊžM»¶íÛ¸gg !ù,#2ÑÀGŒƒ*\Èp¡‚ ÆàA±¢Å‹3jTâ� C†Ì! ãÀ"Sª¤hÃ$+cŠŒÑч̛ @$€³'ÆmøZ±Æ@D“òx販ӧ&_x˜JµªÕ«X³j¥úBêÖ¯`Ãzè*¶¬Y®^ϪýJ¬ŽnÔ¨ú6î\ª:ÊÛö«N�i=üxpà¬}·®º˜jã­‰µæ� ]� îN�œ™ïá­;²†Æ:ÚóÚÓZ#£^ýG××°cËžM»6l!ù,#2Ô8 ņƒ*\Èpa ‚ nà@±¢Å‹3j|"� C†T‘ £À*Dª\I1ƒÉXʹ¡cŠ™8A2€ §OŒ* ,�á§ÑŠ&®8Ê”Ã×P£J5é ƒÕ«X³jÝʵëUU½ŠK6زhÓ~ «¶­Ø³cQ €V¹tí^E!´îX¸bw d›Að† sì51VÇW!{eÜ•ï€'î  ×ªeÎ{-Á•ôVӡݪîJyµkÀÊžM»¶íÛ¸g!ù,#2Ñ8 Šƒƒ*\Èpa‚ :È@±¢Å‹3jd"� C†4 ã@"Sª¤(Çä+cŠtÐ1ŠÌ› @€³'FåøZqÅÀ D“fx販ӧ&+|˜JµªÕ«X³j¥ZAêÖ¯`Ã~è*¶¬Y®^ϪýJ¬Z¬¨ú6î\ª>ÊÛö+��i?üxð€ÀYûn5\pUÆ|gÍ;À@º Ü�JYsä²s²†Æ:úóÚÓ‰%£F]AB××°cËžM»6l !ù,#2Ô8€Áƒƒ*\Èp!‚ >ˆA±¢Å‹3j¢� C†ta £@.Dª\Iñ…É0XÊù #ƒ™8A €˜ §OŒ. Dðâ§ÑŠ)*8Ê4ÆÃ—P£J5Éà…Õ«X³jÝʵëUU½ŠKöزhÓ~ «¶­Ø³cg V¹tí^�!´îX¸bw dûBpA¬† sì51â�„¿UÌ•ï€,î - ×ªeÎ)w•Á•ôVӡݪîÊxµk³-ÀÊžM»¶íÛ¸g·!ù,#2Ñ@�ƒ*\Èp¡‚ p@±¢Å‹3jb� C†T€ ã@"Sª¤¸À$€+cŠ$ÐQ‚Ì› @4€³'FøZñÁ@D“:x販ӧ&,˜JµªÕ«X³j¥*@êÖ¯`Ã.è*¶¬Y®^ϪýJ,„’T} îÜuå²M»U§@¾~È7kÛ¯�ÿ­šìá­ð"˜p½S#[Ƭõñ×YAcí¸ðÚÓU=£^-À@××°cËžM»6l!ù ,#2ʨ €Áƒ*\È� C‚HœH±¢Å‹ ˆA±¢Å‹3j¢� C†ta ã@"Sª¤øÂä+cŠ|Б�Ì› @L€³'F_øZ1Å@D“Æx販ӧ&¼˜JµªÕ«X³j¥Ê@êÖ¯`þè*¶¬Y®^ϪýJ쌤¨ú6î\ª3ØÛö«N�i_ü-Xupà¬}·.<0ðb¶‡³æP€]½îN�œ™od­2²†Æ:ÚóÚÓZ£^=¶E××°cËžM»6ì!ù,#2Ô8 Šƒƒ*\Èpa‚ :È@±¢Å‹3jd"� C†4 £À&Dª\IQŽÉ.XÊé c”™8A€( §OŒ& , ç§ÑŠ+n8Ê4Ã×P£J5YáƒÕ«X³jÝʵëÕ U½ŠKöزhÓ~ «¶­Ø³c}ÐbV¹tí^õ!´îX¸bé dûA°@†æ Økbă±>þ»˜+ßÒÜj@¯ÕË�)£�ÕôVӢݪf\yõê ÀÊžM»¶íÛ¸gK!ù,#2Ñ8 ņƒ*\Èpa ‚ nà@±¢Å‹3j|"� C†T‘ ã@"Sª¤˜Áä +cŠÜÐ1…Ì› @€³'F3øZÑÄÀD“rx販ӧ&d˜JµªÕ«X³j¥ê@êÖ¯`Ãfè*¶¬Y®^ϪýJ,Š�¨ú6î\ª(ÊÛö«N�i3üxpà¬}·®º˜jã­‰µæà]�îN�œ™ïá­%²†Æ:ÚóÚÓZ#£^í@××°cËžM»6l!ù,#2ÔÀGŒƒ*\Èp¡‚ ÆàA±¢Å‹3jTâ� C†Ì! £@9Dª\IцI4XÊ££�™8A>€H §OŒ9 4`ã§ÑŠ5š8Ê”Ç×P£J5ùƒիX³jÝʵëÕU½ŠKÖزhÓ~ «¶­Ø³cu$p£V¹tí^Õ!´îX¸bw dëA0† sì51VÇW!{eÜ•/€=î M ×ªeÎ{ÝÁ•ôVӡݪîJyµë8ÀÊžM»¶íÛ¸gã!ù,#2Ñ Fœƒ*\Èpa ‚ ÆQC±¢Å‹3jLb� C†Ä ¡ã@ "Sª¤¸Æ$$+cŠŒÓ1†Ì› £@d€³'F ×øZ‘ÍÀ-D“ªy販ӧ&Óˆ˜JµªÕ«X³j¥šFêÖ¯`Êè*¶¬Y®^ϪýJ,œÚ°©ú6î\ªpÊÛö«Œ�iEüx0€ÀYûn5\pUÆ|gÍ `ÀºÜ�JYsä²n²†Æ:úóÚÓ‰%£F�&K××°cËžM»6ì,!ù,#2Ô8@C†ƒ*\Èp¡‚ f¸@±¢Å‹3jÜ¢� C† £À Dª\IƒÉ#XÊ™¡£†™8A:€8!§OŒ  €á§ÑŠ!r8ÊôÂ×P£J5ùÁ‚Õ«X³jÝʵëÕU½ŠKÖزhÓ~ «¶­Ø³c;À V¹tí^í ´îX¸b) dkA°@†æ Økbă±>þ»˜+ßDÜ*@¯ÕË�)£õÀ•ôVӢݪf\yõêÀÊžM»¶íÛ¸gW!ù,#2Ô J�ƒ*\Èpa‚ ÖB±¢Å‹3jtb � C†”B £ÀRDª\IQ‹IWXÊY£c•™8A¦€%§OŒR L å§ÑŠXæ8ÊtÈ×P£J5ù¤‰Õ«X³jÝʵëÕ'U½ŠK¶ زhÓ~ «¶­Ø³c­H� V¹tí^µ"°îX¸b‰ dÛD°@†æ Økbă±>þ»˜+_¦Ü(A¯ÕË�)£¥Â•ôVӢݪf\yõê'2ÀÊžM»¶íÛ¸gË!ù,#2Ô` ‡ƒ*\Èpá‚ rC±¢Å‹3j,â� C†à£ÀcDª\IÑ‹ÉEXÊɡ㖙8An€ø%§OŒc àå§ÑŠ r9Ê4Ì×P£J5)¦‹Õ«X³jÝʵëU1U½ŠK¶ زhÓ~ «¶­Ø³cÍ@ V¹tí^5“ ïX¸bÏ dÛE°@†æ Økbă±>þ»˜+ßÐÜí @¯ÕË�)£ Á•ôVӢݪf\yõj1ÀÊžM»¶íÛ¸g;!ù,#2ÔIŽƒ*\Èp!‚ æ0B±¢Å‹3jÔâ� C†T¡£@JDª\IqˆI XÊ™£#’™8A.�#§OŒJ 0ä§ÑŠ@9ÊÔÈ×P£J5ùCˆÕ«X³jÝʵëÕU½ŠKVزhÓ~ «¶­Ø³c›pÄ V¹tí^m"°îX¸bƒ d+D°@†æ Økbă±>þ»˜+_˜ÜH@¯ÕË�)£M•ôVӢݪf\yõêDÀÊžM»¶íÛ¸g !ù,#2Ñ€ �ƒ*\Èp!‚ òàC±¢Å‹3j4b � C†Ä3¡ã@<"Sª¤¨Ç$ +cŠäÑ‘�Ì› c@L�³'F<õøZñÎ@D“òy販ӧ&‘Ø™JµªÕ«X³j¥ŠDêÖ¯`ÃÚé*¶¬Y®^ϪýJl�dä©ú6î\ªuØÛök‰�iíüx0€ÀYûn5\pUÆ|gÍ  ÁºzÜ�JYsä²M²†Æ:úóÚÓ‰%£F�dN××°cËžM»6ì9;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-gray.svg0000664000175000017500000000465715257164523030774 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-green.svg0000664000175000017500000000466015257164523031124 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-red.svg0000664000175000017500000000466015257164523030576 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/logo-splash.svg0000664000175000017500000001344715257164523031664 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/logo.svg0000664000175000017500000001312615257164523030366 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-gray.gif0000664000175000017500000001431115257164523032022 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö¸¯(ÇýüI"B™xˆnIfàT‰°`#²EÒƒ†”…� hÐB"Q�2"‘v¶‘‚!FâB(.ØÁ…)VbáAž#&a`D Þ8Ik•pŒ>j¡>ˆ�þ‡�}Ef‚ÐvD6Ù �ù0™BŸI Š{áÜAVj¹ÉŠIB°‰ ��%©¦(Oúu�ƒs’‚VA:zEcž¦¼hP�m(*Db¡HšJe”A:Š ™ú„É�AšZ*ÊŽd�¤ž®‚i>#y§©¥®Â¥>yž­²bC¨¤Z­®@ZP¥òPyN‹¼²’h>iÂã+¬ÅÂrê=ÿÚl,ÇÞCì1ÒN‹ ù*LÙ¤­,Ì™“ ¨úì:®,{âƒM¶ë²«xÄÀ¯,#ë9÷Þ’¯03âÓg¿´<[Ž—¾k.Á¶é3!/ßÃ*óþ°™Ï�ÒÂ�½Ëbp9¾” rÇ·ì[ε¶D|Ž·$§Y¸ ›Ï-çò/.7×| úKK ú°¬3,9ÓRôÐ £ZK»ü"­‹Óg• `õÕXg­õÖ\w­õ°t´+cÇCÀ )|�¶ (¨ýÛnývÛsÇM÷ÛwË�·Û|bç#®³Ì6„ƒVÑð0CªºKÙð €8G€‹šÏĬXìxCL>‘,i¸0Ùù˜y‘äžGT¹åød<ÊÔ›7„zê±Ð’èå,~ rødy:í­Îú¬­þ;ð™pÓæ ʲÎ4;òù/ÒÇã`nÊËÒS_�òþ7DB*ãC/FÓ{Žõ×c€ëœ<À<90£¯~>¶OŠÏz§¤ÛRú÷#ûF5|hàêÖÿxðéïrżn@`€#™8F'.ЂåpàÏa:Q˜l¾cIˆÁá5­ƒøpÞ?VÀü%�÷€_%`-œÐð~-ta9ø …�#Y*a»bFJf6óý°ÄÌØRÎÔ�3(™<˜‘Îvº±›©V9Î áÒŸQ¤fWdvŽFš¨K==Ћ SŒ(=k Ü�C�‘À‡²è•‰N®¢ �¨ �� Žœ�L6Í¡®�Þc¥‰t%h¾y²I`/1&EèÎÔOFsœuA o€Ò†x©�xêd‚þE"„`«Aã.F‡z…!¨  V˜5c=kZÍJVÜÃ!0ëZÑÊVº" èAþVˆªó)¬ @ØÂö°ˆeÀ9f Ä:±$ÀjmfZ$6€)ÒÀ9* Ù ”\>^J!ÍšÃM­�ûŒ ‰ƒ.È´å0@gCä>”)’àÄlÉ�ZiP£�¨c03{Ùú¶¨‰ø,TK[ÜÔö¹ˆÐjn™{ÚÙ†h®MD>‡¢ÝŽ£·7¢,w!2¯>È»â0î�”»\ÑÞ£„BoÔ#ƒÞ¤"rfŒäKß —ºKDïv!°yíÀN°‚­6€þœ£€[°„'Œ`BXù¨_~ÏQÓZ@AFLâ›øÄ(N±ŠWœbà!x‹gLã£XÄ ÐÀ 4À€‡Mÿ7l’Ú‚9(¥’‡ÂƒìÂ}8åa9he /ùÊ;9À…idqà-Í…b±Læ˜8WW4‡$ÁÐr�–V.³œ;ræPØ÷oVÄ,o1æ9ûYuºHs9Ö|ì¹ÊNôAê ÷ÝñaÆEŸMéõZp��29:L‹¼¸Ò G¶<‹TÞcÈî¬.�ƒàհ޵¬+|Aָε®s}ƒ]ûú×ÀÆu~€�`þãú.j.  ‰dêBˆ¶´§MmiOš·&@µ·Íín{ûÛà7µIÀ܃°â¦¶vÁS7û½ò € Î�Œ`çÈCîl /"Œð͆¼Ï{ãÛõH»Å‰ÆM÷7GÁ1rðrT€!¦Ö&#P�]bh`Þ78½rÔö*¢äñy9&~‘Š“#á)9p�òx|œà"G8CèópEð{¼W99XÞ—�æÿçÌ´UlÜ<äU3º8�.�?ÒM‡¸ÐÇAt†H=T�G>Ì›js ÚO_yÎËvm,\œ&r9h�ˆ«�â#HÆ·ÒZkåþ‚ÂxÁèm`Àÿø†@ ΑÃ2°"Äy‹3 —Ï<æ7¯ùÌ#à*À|ç9OzÍ�˜¤ö´GÏúÒ_þÓæ`€ìKO{Ó¿ õ°�ù$°«ñX0`J¬p¯ 0ÀÜ <”]‹§.Ý?gx-�¯Ä„àñŸ€òèdAWϧ®…¡óÈÙQÈ—�m‡E—`[¹“ãË¥ªV*0>Q ”é…ûÈîþqDþ£0~nT~ö‡·Jç@eïÖP�õ òÇ ÷—Gùç eYŽ�Q´ð€E1|£0�nT�­ð[¹ô}©À�D� ‚Q$‚¬pQ‰à>þˆć ,¨D.¨ {W O£5 (8*ø 7B9˜ ¼Ça>x=( AØX€˜4ç`g÷$è„¥4„žP„t„¨�a—�„=· OÈQ(�X0&Èšt®p†;Á…�à…èW ºÔ{}'1ZH�5x v@`h Ä ˜uf¸…h �x?ƒX ó„ Y r8i¸‚kè1mØ;‡¬P‰2A‡œÐˆêóˆ£ ]e¸‡çZ'Hƒ˜‰�³‰—%‹¢Š1!Š›@ŠÞcŠ0¤„š t±`‹(q‰D‹rA‹Œ€[æ�g­H~‹X ºH=¼ 0xˆzˆ ÂxÄØ…þÆH:~Ç “Ø Ùh¸¨ ш<Óè â5þv Ûå‰Í‚ÏH ç<é8%ÈØÊ8e¨ˆ¯8…±Øq•P�¡0ޱ�uØ�/èlžàp©þ(…! ¨Xíˆ ëw§`���£¨�ª@�ªx�éŒÿ8‘DƒMø ¿u…%)�'Ù‚))�«D“‘j�Þˆˆ²d“ À‘ᑹ’§P‘ï7 ë(W‡�8‰‰:¹�>y úðhñØ‚ó8 õH;÷ˆ ì XDc—ˆ&)‘2Y<ù8�—£ @9BiŽDI ‚F% eˆ?Ù”Åø”©À=¦  iÉ”~“8h–÷0þ—¤�2P•JTŽ™�•©³•”ð¿ÅŒ¢ tû” k)mÙ˜o =³ o�w9–9‰’¨Q@™D5¦�™±™6Ø™Ÿð™™#’—àšÁ˜±©—¤Ð•€œ°*­éŠd9˜a˜í×—¶Y ¸yº ˆ²É yxõ·“çðLžÐœñœ–p~‚xœ Óù�›pˆ}ÜÈ›¡ sÏG˜ß8 !àF:À¤ }yDŸÛƒœ³Àž¥@ 3P0 Z z š  : <ðp•“x1° Z¡j 3€Œ¶¨Y� A¥P`è6¢ÚF¢`X'J‘¥¢Ú¶¢&þú¢%Ú¢Z v¢1j£8ú¢)Z¢9z£0Ê¢1ŠžžY:µÀžè4²à�ä¹ �ƒ¤®à>°’´à›—é¤�¸œýƒ¥Vú<„ ¡It·¥ÖI’É'bêž÷Å ¼s–gŠ FI3Ó¦¢àMZ o7†I§ž—äпÀž¿É§¢¥s ¨Ùž„*LZ Ȧ‹ ~� 04©›ð[°§¿�§âP¥˜zÎgrÀð¥ã€�¡Š…wê ¾™š©J šª¨Ã ¤fw/ J'’7 súª‰ð¦-«ê«�”ã°”Úp½J¬‚ÀqÃJ «Ì*ªËªÕªþ5»ª ~Z.©±az·RÀé4Ñ€ a¬ã@htÚ&8Ás÷ šbz°­®îj¥á:¨·¤ôê4´º2LÄ)|Ú¯ç¦ ¦mŠø8q¨NŠÊ2±¸òNú ®³~è0QÒ1¹: ŠõA2±¯v‚ßÚ,‰*q ²hÒ/PJ+³‘/ñ•ávé(q¤áñŽQ©%ÀJ¦áá±å³yÒ­Ñ"ѲE"{­:2s2²X{# […Z¢µä€¬ë5T™"/[% `+šü!¨H{#,ÀR«ãiFv(n»Œüqµæ ¶‡²�¡þ‘  ±ͲD[Ééy{*ë(|{ð¸-�€±Uk)°¦Q^‘g[˜C† ’¶Çò'�°‡²Ùj#Àº¿Àq”«-¹øp·+ �¦ë²1q·Ç�‰{²gšºñÅи!´Hºœ‹�¹½º`ªóÑ `½‹fµÛp‘|zØ+€#`½˜#p¼(1²;4 p´3A0 Ö0 pÏPàýjxÁ¾„­Ô³j± ðê |Ì@¾Ó:kj!¸| l0¿ÄÊÊ;ÂM[½! ö jð°-< 1Âò;çЫd�_8¬ sáF üïРTž3,lÄìpÁ$ô"ÐEìÄXœÅZ¼Å\ÜÅ^üÅ`Æb<Æd¼ �;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-gray.svg0000664000175000017500000000436515257164523032064 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-green.svg0000664000175000017500000000436515257164523032222 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.511496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-red.svg0000664000175000017500000000436515257164523031674 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-gray.gif0000664000175000017500000001460415257164523031635 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Öj¡>ˆ�þ‡�}Ef‚ÐvD6Ù �ù0™BŸI Š{áÜAVj¹ÉŠ9B°‰Ê‰�Ø%©¦(Oúu�ƒs’‚VA:zEcž¦¼hP�m(*Db¡Hš U”A:Š ™ú„É�AšZ*ÊŽd�¤ž®‚i>#y§©¥®Â¥>yž­²ÒB¨¤Z­®@ZP¥òPyN‹¼²’h>iÂã+¬ÅÂrê=ÿÚl,ÇÞCì1ÒN‹ ù*LÙ¤­,Ì™“ ¨úì:®,{âƒM¶ë²«xÄÀ¯,#ë9÷Þ’¯03âÓg¿´<[Ž—¾k.Á¶é3!/ßÃ*óþ°™��Ò‚�½Ëbp9¾” rÇ·ì[ε¶D|Ž·$§Y¸ ›-çò/.7×|ËúKKú°¬3,9ÓRô.Z%­Õ,GÃÒ.¿½(-µÒ°xà0-pVõÔ\3E4³ø‚�t×d÷õ5>â:+öØe·-,ªºKÓ8»m÷Isç31+ËíËÝ€w‹† »¢�™¿®x”†kÝJÖ~ÿ½øäk·Bx9£‚>Y&NùçPç�­ªTÎKäA¡*o·«,;¬0¨O{+�³·)/ÃmWª‡�Ï ©lŒ½¡ížTï±Xã¸) <]ÌŇŽò¾Ÿ³Þ)éÖküô³¿b5>þ·ƒ.ëÃtϨôLã#ç(óc>KÔ“›ÏÀt’_þö©¿ßø=ˆ‹bò8�»úd‡¿ôéÏIö`á¸W@YŒï»ä½æ�‚¢³ž(6Žd)pdÇ;`+L· V„ à,jŸm¢oç¶0?Ž0~šp9²aÆàp|Á‡:aÂî�w=dE>ÜÔ‰W™ƒ<¤¡¨’¸Š"RB‡ãPŒEòÃYâ‚;à@ìHFUäg‹�Ø£�QH   °$- ÉH6 Ž�èÄþaLV⎰àY#aE#fQ àÀ °ò•¬¤'~ €q¡DäÙÎÁG,ï]º, Bƒx¢åø@ p4JT”Òß æÖüˆˆäg4�9,ÀLi¶BeGì¦7Ù¶ÈFÔ² ðä%Jp Ò Äc9è÷I|¸�‘〠"uVÂ2¸‡1áÙLgÚ3—`„G!÷Y�~nb˜÷0À%â ‹ÿ‰_,'>©y†êC›hÀ9ïÁÍzŽ“®Ó($8xÑx,”Ÿþ”Ä8ðÑwN‚¢»T©/ñ�0`rÔ͇D3A„&ý©<Éтރf.=éHóáþÐK Àšq§P Š E’£†j‰À "%�`]6J,¶4ªd„bïQUJ €�_IYWS\nC”,ÿdˆY�&b³ç`,% *¤ªö¤­0¬Ð�¤M¡R…i%pŽÀ¦Ž˜¬+2ZuÝÖ»ý`8![sÈV´�ˆmwŠ[”æ£�‹¨]@ iVÎŽ6 ‚GPp^•wÊçeåqW}\W¯ÙårEôÞRœv¾�øïxƒÛÐöÂ@8‰,“Û_þRñ¤X$,p3 €ê–㾎àJf°]þv—†më9ØZ>¢¼çèì#`T”—Á^sÉáÁ4JlÀF±ueJS™tø¯ \ɾE  Ç®E„ŽË¡âFä÷$é´qŒUÁÂ#H"Èkœî‰ ‚aE @›;Ù/i§œ ‚oL²ûüQ BŸŽurG„ÐŒ8øV1n šCê‡8>z)G�6þ`q–ĵÉm^°ŠÜ5ÏÞíA€y&J_DÅc®p¨Ù)'Çâ¡ÍgB<Ù$ø{½çþôUˆòÿ79Îù�bìôñ$&Oú²« 9ÄA‘\øB4`1y±àGOòÞë"¥˜ƒ>À}uã¢Ç&qÁ@%Aûâw^g7GÃ�Eæ3>Ñ‹°àM²`êŸæ¶ò¯�ˆ¸¥šŽÂI"oΙ?÷ý­8=YU oÊ::+à€Ögçw· òÕxˆ�€lw€žçpGpmV{˜2€Å€¼Ç~…0nÇdôG€ Exx ôQrÙ7‚k&VŸwT— wÖçz¼ÀRàþçâ•�0˜ Z!Z—0xˆ‚µ`Y×£|tg4Þd�…`G LGyý÷ ùðlj7O6¨€‡�Jñ1G>ø‚Vh ë7›7…äUáV�… V�‘P}臆³ kg€ãpfUvWe,°…ã6v?xƒ¹�€ào‹ÀRU8ƒ�Шlxj!ç…ˆ1Ê¥k¥W7†…Tõ�Ôå‡� ÿ‰�p‚€8C†&Rù`|È…ö—xø§D¸&0¹¢m±[œÐ„� 7vÈ[÷�{¦ô…8Pç0}™€n·ø ,$…§y¾(9Ú{瀚 øˆt8Š·pHç°rþ¬ˆ¬GŠø÷eæÐi™€Uú …o‡²`X5斊਎¯‡L䀎œ0‹ø@�XhE8 ˆV ÑT‰v5eSEŽ™Ð%°J®$(°�±´„ËØŠU´l,t†@¨ŠÈI©ˆ�DI ©€Š�}ÏD rXVæ�vñx‘ŠH=0�ûƒ+½@‰ç0†0v+ù}š�l™¿(�z�ð�>‰^gs�¸}tÛ 8)‘AI•ð%5ÁP‘Ty #�¥05Â�T©7Iy:` –¥ŒDø„å•샕Oifh H9oVI p™•\É{i@wù–I3 £v—diþ—òxCKSj0I†‡i3ý…‰©˜â “’ ƒ­ŒKå”ÄÀmðH iÖ’†¹˜ˆ9wè–p’fiš¡P/I» uÙš˜™S3q 36Qi ¼†�cõ—õÃØ`™�ð™*Eª f#›¼Ù”Æé›«¹Мñö îçœyœ�ù6Ù0˜Ïó ¹H™Îh�Ù‰Ñ)�âyš}Y“�y�xs™âГŸ€hFÙ›ëY:äéž³ù‡Ë© l‰P‚ó}ùÉœ¥)š«`’QžÚÀ�äÐŒ’p›â0„’ ÿ‰Ÿ�É ú€xššª œÉ�Ÿ ƒjINõÙ–a‘#z�¶Ów'z•ñvþW§ðŽ!ê˜ØF-j¢ä�›Ÿ  …9F�i£ŒƒŸrõ•ø�ÊŸ7* €h.9 2ÈwFX£%$‡(ò6 ™·¢ö)¥þÉ1Ú`XФxùKJ $ꥡYWJ /ª¥gj¦,ЦÙÀ™À Ú£eºŸ[Z'Uª‹¯�’n§pú›| biÀãç°¦U :&qúð�©p¨�:¦Žú¨ !ˆš úðš™y©6‹Ö2 šž ¤ÿ¦‰šeå0£Ÿzª÷€ >”Þ÷ šÚŽ� «é¹ Õyž,É¥]ª«ÀjrùŸ´@§PêŠÂú£þ‚š{ʬ„´¬ƒ: a8C: þYZs94¬Z©Ÿ  TÈ­ÝJïv ›³«âZ à:µj T­éº8@7·P­Ö¯«Ð¦Le§ßTüН”ÀBÀDÁ¦› °wŠ®§ wôаSª°¨°>@ ˆ× ö*ÉZ±Žð‰z ÙJŒê±’p±Û t¦&« ( ½jóÙ²Ž ƒâ@ˆ* ¯5³®ãбyê«5ã ã¡ð`rJ³ˆÀ { ð²HÛ ²z¹´OûŽã€³ð ¯áú´†€²°­ q+þʵ…p’ B;xç±m‚%xbê°XË^»¶øêµušBù·é³ Åþ·{g²zû¯ð0²ÚZ±Q·ç³D‹«Z¯…z%fË­¹ˆÃ�ªc³=[õA2�·v‚3Û,+íZ�«Å"°«7$Qdµ %1.­ë"œë(< ´#‘¹å°ºj¢µ¯"qºE"~Ë+ë­Êá¹Ðëˆ{"ÒKDë#Ï[š"©[>k Ù;­“¡©:;9ÀÊ«”$¥‚¾hç¼êÁ+ÄyàéÑÀëÐ,°›á©A!¿p´.Ýk=¤{Ú(мž²ç:á¾ÖK1SÙ ã‹ ð¶!† (`µ!ì 90'þ‘Àã"Œ"ÀÂÃð(±ÁÓbÁùÀZÙ@ºëÁñÅ@ÀáºézŒàÀê‡ÃœÂµóÑ2< ôµÁ:ê±àÄ*Lœ =ŒpÂ:C¿+àÀÒ` “0ð !ð  ·&�cL³M[6 àêð  °È"ÀÇ<ñ[L¶‡P¾ÒÚù+ɉð•l Æ˜Œ@¼ÉqFŒÉ–ÐÆ¢,À¸¦œ ‘ÊçÉ­, ¸ŽZå:ËÛFÉRsɺ< ð¨15PÊÁ¬± �Ž| €ÀœÌÔ\ÍÖ|ÍØœÍÚ¼ÍÜÜÍÞüÍàÌ�;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-gray.svg0000664000175000017500000000472315257164523031670 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-green.svg0000664000175000017500000000472315257164523032026 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-red.svg0000664000175000017500000000472315257164523031500 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-gray.gif0000664000175000017500000001176615257164523031330 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Öb(Ä# e±-Á8{�€–u1ȹ¥™.=©]&‘(•a!Ë €êtŠJXZ2­údWÓ–W_q]–�¯D\0ZÊÞØ" ri|[âV®¬t�¼t%+ï ¹øå–ÛÁ&ìuu1 À¯p:N! —äÉ1ØŽš‰ùÄÔW,;¿ƒ…@t‹¥®|™6Aìêš1¡zMâgÚ,^¡í\˜ThÜ1*—ú-ú�+à6„•2íŠ8r®nç�þ®×ãZ ú ½¿;ÿ>ˆpQÎ –JZß¾Y‘ ü×ßçé7JiþÔ%ÒUCJ‚G·Ù=BxÄg¢ìÌ(àYè)�%3Êeq&!>$JŠ%1A…®Ø"'^LYëÍÈD|6Òâ]%êÈ„0b$ÊPB:A¤($>¢^ ù%©†—ð·Ê(RFQc%�`™eTVe^~…„ âf!šI–(w(p¤›T,XÈHpÒY'&x‘Q¦žSä9Æ�wj… `@–¦¡Ul9H‡\< £Wˆ2›ŠZÂ"¥O8*H"^ȉ(§Sx*ˆ8B*„2&Ê-ºê¡˜¨f…¨—9+­=b1ê®U,iI“S´&+°•b⫲Èjñ+vªÚ¬™þbë^²é´�2+ųÜV±�¦Nî©ò­·çf�"åÐn¤ì2�É«óv«©ÙZ’/½—\{Dº¸·=',Ž˜èšD½H(,ñÄÅ8­ !l!øHñÇ ©D¼Û � 2ö†¬òÊÒ6ai¸ÒçÁ,׬r§å–ìÃ"ð6ýqÑD| ’. ­tÂQT;ˆÎ;¬9HÆF,mu:ëVé|Bqõ×EA0Õ;`Bv`§ýŒ¾Ñj w¥öÜ»°]¨ÉÇ&M÷ÞÛ�©­ýrI4ß„ÿéµ%‚ñ¤• ƒ[‚á(@n‚ä“;>å)ûÄz÷ªæþµY.è—‹éI”K¬åf—µç) ~ºé²£.»Ñšûг¼v ¾Bí´›>;ç� �„—\Êñ™Ã¹ð¶C<ó�«ã�ƒ0.7õ¥s?úôΟ½÷J�ÃîâJOþøáWîýíHHÈÙ1˜´úíÿ>þ¹ÇnüÙàAÌѦæõÏëߟ·?ûáÎw9@ÓÝz7Aæ/t ¼`÷48<.Ái§Ú� Á+/ƒ Ÿ!8…�‘°l`gÈÁÖð2,Ô^ð·ôÑ0…î»áY¨B>°‚5¡ºJDý™p…H,¢c¨„XEñT<"¥ØD N±‹þcÅX½©åà«;ܯèD1ÚÐ�8Ì!!ÒX?KЯ|üÛbá¸Æ~.ŽHx›¹l ¿Aúð‰}l™ÉÈ>2)‹(xÒ²ÂßAÑ�D¡_‡IèXbkjD¤(ù8ÊM†Ñ”¡¬ÇÏ\�«I–’�^$%#/©H+ŒÐ6€¤ayJ^~O“¾#*…Ì&23‹ö*ÉÄa³“ ü¢3éÌ& ðQ5$ï’EÉ%Z˜Ðäâ0ágÀpº}®œe7 MuJ³˜Mx’ò`� ×q³pø„'´yÇ= Ú£`>ó¹…c:Œ‡•è'ÍÊPvZÁ…®š�©¶ÐЊrÁˆ"°bþ-›YQ¬š%PE;:P. Ï‘%¸(9IR¾q¡"(�¥ â–Ζî� Ç è7sä.›§=¤g%t*PŸ‚� åJf bZN£¦ ©—è˜ ®éÍž:•n] ˜2™ºÐ«ª-«�,äE½:·.h30ÕgSɺ´.ht£+¸eUkÊÖ¶•©©Z)K몴.³Žm)_¯Ö…ebž;‹:X–u�j!»×ÆÖ¬ åZ¥8�4«ZVh…Í[bÑYº~ve˜m 7ˆƒrô´7ã‚a¡PK¸ö�°u,T-QÀP½\n/»ÛJW¦�Øáö†‹Úâ²é¬„rþÇÜæn¡\DÝ, nû”êZW Ø…®%J{OïM¶ª]ä§Æj^�¥ÖŽ’�#{ÛK±.@Tšõ'”æK_‰…ÖùM©˜øÛ_¦qá¾seAº\`zØ’ruèkŽ.(QÂ*xk‚'Lá°mAÃVÁ…Üá†pá¬0˜-‰Kœ�.H,‹Æ…ž%.¾pÅíŒ=ì¬Oʾ=Þq…·€`@�77@’±�[Lä "4°ž]26¸páØ" ²”§üa]ÄÛ4í–‡ñQµŠ@ÅZ³3VŒa$B¡xTóÚ´0ÛHµ�K³œÉ ÞKèU½œ…ó.÷œ‹-T™»£ (þK]Bó9 ÚÌ® ÐY^GZ Z}Œ—«féG‹¹Í,(W”;í,9È%o?MjŒ"ÁXI®Á%R]éV‹LǤ4 2Ía[suЀÀÀ ÀæîúºoVõ ¢ËLcûÖS¨ò÷Õëgç8 =ûóR7í1k“SŽ­Í¯�ímnAÙ88u­ËmN(œ¤nÖaµÙÍjþºÛì¾7Z©ƒÏÇÀNùë~ ܪ(Œ=æÖnˆ „0„˜ãÖ ÀsrÌ+à  ¦ÐÄ[âï�ó;'ÿ·¾3 oƒl\$hüAÄ/òœ¼ÛZ?¨ó‘ñÑr‰�<ÑÍvHÍQn šö`l ?9ÃKþþã„©áA¿y¼Í¬"¤‹äå„Ð5 Žyc� ]ãJß¶%$M��ãàEx’¶Etõr¸Ú“Ô:Ö.’Ô#ìnì¿ðº9ju„œzº´�»Ù-¡Ô}#ÎêN¿ÇÆõô8yï�Ÿ{Ê•ÀìœwCïJÁDá¹vøÇ—ÝäÃjB¼h~y`3R×Á»ŸxËPIgwä�Qå 6Ε`ô¿ÖÙ¡õ³·±‹Ýh(ç^¸Fêo Ù�ÛzÄ_®ï…_¥ä7�`¼>Ñ�ŸƒxѺ ŸÇ;õ§môÍ û1852³Ðø‚ßïݬ�|~Ê'� Ð�~ûquBÜ wüük ~þù_¡ÿ}·2@2¡÷A¢P€/pGÚ”qˆsúÇå�HrÓ7UW� Ö5fÐÚ·’â_��`{wðyp\¨‚xç!À#8ƒ]ðmÛW<¥z€”|,¸�4¡ƒ¿‡qxƒ8‚$xñâ€É·;�€ƒdð„ '€ñˆöýw…íG (¸‚£à‚ç" ÊЀG}¤°Tƒcè|Yè…¦7.¿‡ˆ'22{vˆ&¸]ÿR m˜ f.¥ðz¤w€„mjw >·*ufG„:„‚BœR dˆuB)h÷x£0y%b…€þЈæ"zâ [hp±à&®@ŠDa„íf!<˜uøðˆ5#¦ŠÀa%¢ÏÁ‚×V”¸p«Ñ‡ƒ€¸qŒXĘ=Õñ‰O“ Î…~±{(½˜g¢ÑzØ1#àø\Ž�ƒ „’G“o� `‰Õ¡´xzN1ŽÆE'æHÒÐö8²!vqðø@�• œ¢� 3�h�ãp‡¢Rx ‰T áÈ’ù¨5›¬P )%i öä„ó8 !i&‹P I ÿH* ‡¾Ñ‘K`#™‡ÉW�Ú@`“@ ×H ˆ™“¥°€@I €¤'¹ P‘³"i• P€m@E0Ð4¹ “³—„ 3QpoYt9!s(Ã�XÖ_­¨—iT@•Û§H9PH¡—ôGV½€˜=�‚ɘ(`eY3²`ˆ’I)41a”™)89‘Úphù™P€P™ö'mà™¦ùš°›²9›´Y›¶y›¸™›º)›!;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-gray.svg0000664000175000017500000000424015257164523031347 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-green.svg0000664000175000017500000000424015257164523031505 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-red.svg0000664000175000017500000000424015257164523031157 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-gray.gif0000664000175000017500000001630715257164523031660 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö5É ñPÅ…ç$-òÜ 15%�Õ[…©€â=Èp-� £Þ$€ÝÏ›²÷ô™Ë¹Ö޹• Ü­;§«4 Sçò}ã�ßÞ¯ñ{LÝÁï I㨸LnWA7À›ÁT�&‚rè"%¸Y8Íåò4Ó¬‹$ vì)©¥A¸­$·Ý¼¥HF–5ø½Ò¤Ÿ8�Äò&r£…{n¤\4êP†[†�ˆ´âÝ™HÓüE4åá£øF†~yˆëé«X?öü||,Ñ6�þþ”¶Å˜p|þi�.H�†Œmf�Ìf6Ø€¹0˜2_I†kÅþàØ1j¸ÅzÅP2p‰ÆÇœ�ÔÇ$¨¢útŒs3’�R1úyD_Žf¼gL-$ÈC^‘™†Çx�1À9‰‰º„ÈŒ1ZZ¹ÅF|ðã8ÆHéå`�e.ãx¦‹™gŠ‘¦ê‚Í�·à§œI°0æOŨµ§PS¥,žÒÞ l$™Kаj'£p¬yË/FR‡£·,zH¦šÆ!d.D âA1ö„*c¦d"¦.‚ª*ÇŽ¸`ª¬³ê!·â*G1…àyНw+È|¸ôˆ� 0@Ð ­Ðà,³ YZЇ~$Úê²pº €þÖ €P1Óñ�ì-p^#@„î8,æA—t€Ðë/ ‘)çÎ Œ¶¥øÁjÂÀ4K¹ãB€Ç"l)žÚñî)¥nÄÑ P€º²C¯æRB,óÖ 27Tlë¤x«‰¿|ι$c�.‡Òa‚..Ä:£#rφØ\‡Ó†0�¹`õãDÓ…@­Ãbb:UC{-¶ �¶ºÌ:ûlÀ.›#s $èÒî0VxHæœK1×U8œu717Ms^2/78ŒÍÍãè’j¥‡ "¹4«;wGsS!nÖŠxæ €42|îÎäb54ÌE¬þö°ÄS´ X_4:ðB)Ì«|=ìCÝp1Ã0œŠT} à-â†Xomœ" M‘ÆšáBFcÐÈWŒJn�“Ýð¤€(1>,LÊ¢"õ€3]@”YÀ#½K,ÔÑ'$ .œ'†‹�â~y¸e)úXaFãa å-rÙ†v�h{Pæ)˜9…˜+„«¨‡CÞ‚_Wߥô°M\<0 ¸f<¤8E!”S|ÅÂÁà­Q@ÊO¥ñ-(ÐQæ¦8ˆ…žå�OþRØY r!p!�ËÅa·zÈ´(½hÎáÂ¥˜ã�o!j4ôù ¬‚ª°iN3q$ÆØbxÀ,{áa†Öõƒ³7®OOA–·1Ú[õVøÏ”œb ÿ7ÚÒ.› �3À#1Àå#“�¶mÒ=hlÓ‰êìÀ>£°kV�ê¼@£¡@\:ðô»Op)œá°Ú^caz…T*€$³N Œ±²;9'øÀ¼ñ�­Ú„‹ûB½£PLó #å¸`MŠ{6��tètJ¿€ðS`€m¯ÛµÁ‘ “Ÿ"äO¦½ÇàeRR<·05ŠOÁf#Ôü%}ÂÈ�ÐçþÝ ¨–ÃEa @ƒ8Ú�ôx)Àqo+y ‘󹫧èõ3\t°I(l‰ß Lôráé¸�7úþ`ôÀá·àÀÕ�ÀduÇá”ívBT×Kè[¨\ 8Õ´³jŒ(*!rê¤hÓ‹�Y: > E¿¶FÚ½Ù9öôVK½êS�µÔ«½XŸ‹ LÀuŽÌ¼ Q_µÕûþ÷ëž0'|b%ü{îmð¯Äùž4áY`¬Õ¤?ý‰Q‹x»Þ…°ÆÍcahm'“�ÿ⦉¸ »¼ïì󃑵µbXN~…ÜbànØ-É"±ÇGßÍg Ù´Z#¡'z/7it°F‘”þV:cAw§{¨plÃcƒ÷bv ‘†t§€`mPwBÐJh dfi7‚èNÑVnZ°x¤ dI`? Æc$ã€Is;÷@y(¨p!ˆ èåEK@jt�rlcƒÍ7oÀ£„I“MÊW AU¹ _Ex •H(N¨3‚'A´×ƒ>(…·TZ§`L N¥�@p�rCÐ…:s�ê#†)˜6n.8 µ&Fhi?÷†tX ú&Aî§[u‡¹“ ‡…nV J‡_ì‚ü÷€þgsˆcˆcC%x �¨Ïu„Ž7“2b¤]óR†#xˆ£e ‹x)Šsp‡"þH‡äæ>ðP�…#Å‚YpQSõ}(™…TpÈëGZU§3[µv‰ˆ ŸˆjH ²öÇHŠtÈ„WUFb…¢%¿•_³v Gwá¥HÀ�Z€3˜4É…šChÈ�¦à�l°F˸‹ñ2P‰Õ㔨‚�4b¹ƒ£¦€Fq܇ ‘9‘9‘p‘‰‘ v·€>4;ˆ  ‘“U‘&‘#©)‘w´‹—Ɖ¹@„JàR¼tj‚ å6Ír6ÌÒ“ÍÒ“×ÂP©F›õ,Òb}Òw”Ò"œypˆìgÞs`mÃ+ªE@‘øŽ°>íŒp˜¾!¸@ò˸#u™À8/löÈÅþ%ºP¥e@°•̘ÌFŠÀK AႬuÀc°­¦@D�Ø�…�ÊðPœ{rûÊ·“ sªÂ\ËvìË¥ðJ:³ëÂ`¢A‹][5ÍL ~ÕCû« oüÄ�ò™¬®| ~c-Y)S-Ü6Î}ÖÌ©‚ ÈÄì±÷‡ m“ŒÅ0Ïnã6Ñ÷0üð73³KuàÍ}JãˆÉv—‹¹üΛ4Ñxë쬺`¼t0Êel4~Œ‰×ÔJA¾R¬Ñ~0;(�È )š„tL õ* ÌrÒ#ˆÍÜ Ç ð[ É{3½3|Œ)º°�wP?.mÚ¼�ÓC= 5™þ�Ô,3/ô28Ý€esÅâЉƒúI Q|m}Ò‡5­g}éòÓw@ɤÐàͤL,¥ O‚üÍzÝL,ý³XÖ� Ô×ë7= ö{Ø‹QÓ…pÖ£PÒŽÝ;j¡€°Ø¢ Ç•ˆ‡Ù€0ʃÜÙhðÙ€-­¤¾bÂL³¢0¢°lؤ½U=ÙJ=س½ô+ �­ /ð/¹�y)Û‡°¦Ü[pÙ � å¶È ‚6 °� tMÔÏM¦9Ý ~R ¥ŒÜÇÍÁ°Û¢‹×m-‚±ªçm¥ò4ÝÀ�Ýæ}ÞÙ�×q „ŒÜ© W "í]õýþݱ Ù£°Ý×� ðM&ÆÐ×~ ÿ\ÄÑàÀÍÜ«‹ ƒ!ŽíÚ¶­µQÙÈ0ß6‚ EÍ(- Wíá(B,ÍM âÍѱq  !Ô *ÑѡѶZ­"µ�уqá¥�â@RÝÚÒPâþ! ù}¢MÍñÁáPnn i�RN ½}O^ œ'^ ”ýY^ÇË�×‰Ý .À J>Ó g‚æDË]^ [~$z{ ­;‘À «@)Àã±<r~ "î$un pèíà¦ÒÀMn%àÔÓP ‘aŇm¬Í1æšë ' àGòÑm %@êþàÃpŒ*�è·p¯.Ï€ž®*™ž Yˆ è òßBêÜð…PèÅ ãÏý”Ž鄲ë\"ìH0ýÀPëb@ ÊÍ ÄžÒ °%íX%ìçPªžÛ ðãò`P –0��`ðõm8�îÔ>Æ€   @ªP  ñ'ðüPà^ð­Ó(¸ç/²®ñç@ïÞñHÄòèÀÊNò\ ï(o �à,Uÿòî>ó#¶ß(_ L�ó#]æ/Ã$pñ>Phñ2°òEo !Pó¦@@@ôMŸõZ¿õ\ßõ^ÿõ`öb?öd_öe;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.512496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-gray.svg0000664000175000017500000000606215257164523031707 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-green.svg0000664000175000017500000000606215257164523032045 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-red.svg0000664000175000017500000000606215257164523031517 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00112 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/safari-pinned-tab.svg 28 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/safari-pinned-tab.s0000664000175000017500000000262215257164523032354 0ustar00zuulzuul Created by potrace 1.11, written by Peter Selinger 2001-2013 ././@PaxHeader0000000000000000000000000000021500000000000010213 xustar00113 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-gray.gif 28 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-gray.0000664000175000017500000001221215257164523032565 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö445@¡G 6FÀnŒ�¡ @øÑÁ&¾©U°e¶~ èaàÁŸþM`ô TD€‡”éüÔ4�‚ÄC¢+°´PHt2ÈÔ$ �Ôo ª’W€64žK vO¼oÌâÛwNg[;çX*�÷Ò"T­ë÷Í7~7|+¼ð± v Ã&­#ã2 º)ˆ|Y‚ Ýws5ZË£‹\è¶ 5—ÌÓP»." t4Ù³§¬–v ·Ýzû–BÙÖáGøJ£Šœâ4 Í›Ð�&.ºsѬC)nìšö!ÒŽg"�sôј��™ú沯¯‚ýXôôó±D{0<šøüS¼UL¹É Óé¢TjÈàv`Ètfàƒ]˜‹ƒK!…`þÀVŒ^ƒƒ!‡[´WŒb!#‰a'ð<´;Ü9ë†.(:i4¬NŸÂí!=O‹MÒ¥H‹†b¸l¸sÖ7Ž�‹�jȼ'Ù6Sk) ƒÑh¡Ù�Ìv47#î(@›¡‹½ÁÜ}ŽØã�ÞârëÚ‚Ÿ£÷ÞLÍÅo�x,U7ŽŽÍäÒÚ«®9?'ã1.}ƒ!ê§“Ž^2ãù�Ëþâ™`íºA¿”+б6²ÎÔí4Ë’KÓb¤;ŠØ±o4,js¡ð!™+ÿtÞ…¬~‹äUD¯ô Ò»Þo!ž‹Ñì(‹úÑýù- Ò<„ï�þûVçùÁ[} �îï¿)&𱾕…ÿöG@RpNÈÅá¶ð?8ð�£à-`Ô…8™¢ùƒ`%µ-0}zÐ ÷`=wu¡�á3H·-$îM‚– ÷ÇÁžiÁS6 á i¸‡Ýt 8Ü^vÈC=,~W@a ‰ø¾>ˆL Od!¥g¾ö]át?âÏç‡vÕŒ}HØ»÷‡\ð® Úªâ•WF\Ðo ¾«áþG÷ã ð %¢çx»?ÌM�Rß�ÈÇ>¶1‹RÈŲöXHÍ�½°B¾þ`»F6A$Åi$Æ<ØÍ’lÄ¥JA;( §“xX(ï|�‚FR°#ոʬð�ˆtÂQK¶Q¯\ÅŸ€ à—½ä™ d7$à"0£e2ƒˆ<–B_p¼E3%6Íåâ�êšÂÞ¸×MeªÏŠOðâ YN– "˜ë|¡µÀ-!’zÚLñ~%­)Üš[Ýzµ�ƒ,ÈjZÝÊ–àT+Šù;Ýj¬§ «>³Êþ<ïf"»WuÂÍLêYÊ:£²Hdy‹«‰¡^W C�Elaaß(ø�½íÍk,là#8V¿Ÿ�… q"X°¿ò ×h¶n¡ÖÍÐó³ÑTËuÁ¨éuöÇ!í.h»aîõºË­ì�€»¹Ldò¤MñF,¬×Κøä ÉlS\ú N†v�E8³‹¹ßÝÖ`ÔJ¡è/¬Òú."¿ŽhŒg|޵¸KNrMP›€îh8ÜÆÐnQP˜L+¬øÊ“˜‹Ú’C0wǵ�o/,˜Õç">”F…nœ�øè-)J†þ"=é†\z.îEü,dô™£Ó†‹ˆÝu9ÀJ¹†¿6@§ãÙOQpª1]/'›@d¶÷’ \àÓ´»O|#ÍÎÇаeç}}ðìûã4èÊO*�(/Ö_Ìb-á{ç×ð¤Û{c÷Zç.XŸfÈçô'?=è ÿíhHÞ#� @Çé°ø·�Cá;×yö]Ïü‘+ŸèÕå îÌG:6~úß�é‹Ånªo‰áø°RlŸ 2/…Vó°Ùõßÿ % 3x~â÷q u¤ðÀzŒgþ€n}|×zøwÛ7åG�èèåws8õ7~~T Ö‚SàcöåWw(¨õ§�|°x}÷‚`ƒ°�|¦rw‚l°`¢c¯¶‚68ùW 'xSßW„GPs£‚š@ZÄPS¨ €ƒTHW ÑG„[h4 B X <†BƒæGyR �·�Ç@fÌà„£°Z[ø"ù@`�¦†Ep P†ÌÀ†xø‚lØ€c ~·�:hB.ã%axˆqˆ9Çp~(ˆ ‚{¦�—x À‡yp؉]8zÆ@‡Ïâd¨Pms-@8 I|�.ÈPˆèw iþx(( ûÇÑP|�ò…ÇS È@lo EwÑpuPÒ‹ð±Š`r„þǨ¨or†æ¦Òp‹" ‹˜4xyÒŠàøHJ"ޤ…ùñ�¥àsë‘‹H¸#è¸[ÑÁ€&·#Ð ÚX¦g Ç%ö¸m¾ÁŽ¥ ŽIroÇ qU ݰ �"ðV °÷y ´%i p‘îðeÉØ�X‚`Ó Õð�c÷,í÷¯xÀ‡Ñ°“˜$0†Æp3ù#0 èÀ‘œÂ9A>)Ï�-ù((™ ÿˆ ‘·A…0Ù q!Tôà‹((#) ’’”±è‡ØPþ@0”b@ *è ÀuTÈ^ À•X€à”éÐ9é€Ð,³ Ð –0À�� P‡x9A—j˜…lC+A   pª œ¹�Ù À–`™óØK 9šN”|¤{‰šD0QÙH`•®™~ÙH �‰µ¹Û°Ez¹›ÒˆèS Ÿœj@ÒC šÆIÐjñ4 @›Í9�ð™§ `ÀœÕžâ9žäYžæyžè™žê¹žìÙžX;././@PaxHeader0000000000000000000000000000021500000000000010213 xustar00113 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-gray.svg 28 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-gray.0000664000175000017500000000402215257164523032565 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000021600000000000010214 xustar00114 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-green.svg 28 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-green0000664000175000017500000000402215257164523032645 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00112 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-red.svg 28 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-red.s0000664000175000017500000000402215257164523032560 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5134962 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-gray.gif0000664000175000017500000001435415257164523031646 0ustar00zuulzuulGIF89a\\öäääÝÝÝÑÑÑûûûñññÞßßöööÜÝÝðððÚÚÚåååêêêÔÔÔåææúúúíííáááèèèüüüÙÙÙÞÞÞçççùùùøøøÏÏÏäååÏÐÐÒÓÓôôôíîîÈÈÈÛÛÛÓÓÓÄÄÄÕÖÖóôôýýýïïïðññòòòîïïõõõìììáââÄÅÅéééÐÐÐÌÍÍÚÛÛéêê···ÖÖÖ»»»××ש©©ºººÂÄľ¿¿ÇÈȺ»»îîî¿¿¿ØÙÙ¯¯¯ËÌÌþþþ÷øøóóóëëëëìì¾¾¾´µµ®°°ÇÇÇ®®®×ØØàááÎÎÎ���ÃÄÄ¢¢¢µµµÍÎι»»àààÀÀÀ¥¦¦èéé°²²ª««ÜÜÜÁ———«««»¼¼���¥¥¥¶··÷÷÷ýþþÿÿÿ™››™™™§¨¨ÀÁÁÙÚÚ³´´ ¢¢‹‹‹ÍÍÍØØØ¹¹¹ÁÁÁÆÇÇ‚‚‚¬­­¦¨¨­®®ßàà­­­ ¡¡¹ººìíí!ÿ NETSCAPE2.0!ÿ XMP DataXMPB?xpacke86D0E6B" xmpMM:InstanceID="xmp:xmpmeta> !ù2,\\ÿ€‚ƒ„…†‡ˆ‰Š‹Œ�Ž��‘’“”� —˜—*•œ�‹*hje¤¥¦¤xjq›ž®‘yx§µ¶¥xh¯½ˆ²·¤tjÄÅj´Áyv¾¾/Ȧxym 'dרÙdzvm£µt¼Ì•ÎÐÂqzÚëìØ qt§x/ãä�ç¹êíüü h§èL°çHEStÚXèǰŸ…6çÔœ ¨èE´6 32| ÏІ,|#g¡Æ“üNä1•§EÐè4@I³ŸhuZœ �Éš@׉ĥ“ÙS‚*m××@£¸ö-�š�g©§¯Ž¢’Jµë6h ^Ù)E‡«×®z�á)J æÖ³pÿ¯¥EåR’…ƒeð˜�ÛµA©:œV’²Ã·°Ö2q(ÆX¸0@Raa!SÓ¸±…;Âê2Œçgå¸zJ½°tõs妤&:úFÙ´åxeÐ8:¼×õÙ±�áEcûó7Ù‹ü¦î]YxÕ‰ç!þ3bE'JÍdÞX+E„QÿŒl"¼¶W~ ÜPôáâ ã¾~Hë�ô•‘E.ô8üÆ�G‚=½¦((à€X Ai¥FH¥…rD(á„Vhá…n)†÷Pf\(âˆ#²�P¥ 'ˆEe,WÓ$Æ(£„fõÍ|+’ÂJnÌècŒoÔ”_!ß$…Òƒ?&)bÿ†(¡¦ß 7Ò…’TVȆ(e×"‘�¡G•`NJÂ-H2µµ#l¶éæ›pÆ)çœlFpR™…4xR|öé矀*è ~¡ž„è©Q0Œ6ê¨-ý1„è °¥ÉŽ);\P¦ÿ”Á馚>úh¤ýL*H”‹–ÒFi4µj«e¼ê*«¨‰Ê(©þ�b&”]¦ªcŸ±ëg°|ÚJÊ¡š‚k;ZæÁeFfdÊ°Ô ¬±†^s†²9Y‹;64­µÅV[¬±ÙŒTƲì I‹.J«*¹®Òkìº×\°·ÝXˆ‡'�[®½Ã»FmÌq »ë¤XˆŠŠ«ê%®R«ÿÅÔ4Pë½µ0œÍ†eŸ¯e¬Á§Â&'\òÉ+«Ì1¤)xH}$¿lsǹ[ˆ{‡fâóÏ@-´Ðd$ß!çw_\ë%ÞÒp‘—ˆ–t@}Vw‰$í±ÕY·ˆr\Oå\bŠgMØ@™ÍÈnhÛHJyŠÐÖ6J¸­»š®skdlpWJxy3T«Š›¡rvàì€ü¤#L†8;—e&Éb�kó˜Ý“Va•“qØâ°à¥WåÆƉ[yY õ\yi&IÝ£ÏÍúZYEÕ6ë˜×þ×V‘‚U/‡Aku­xüîKï±yFÝP¨°Å êeÈ$ÞM9Oó$)ÿ™J,¹N‹¨(dhG¸|Ò!!$>_ä—"ÑùØåƒ†ê4uŠ�ñðbN@é(õl¸ Gþ÷ŒZHƒ‡k7¼a‹pxo€qÓ\�QŒsÔBäD,fq³\ì"ƒ½…(•ŠUX„ÌÚ QȺ�…�!ù,$2󀂃Z]†‡ˆ‰Š‹‹Iƒ��'\_”•–—˜™šZ��`š ¡¡\'�ƒU¢©ª•`¦Z«±©U�’²· œ�]¸½˜\�I¾Ã–]ƒIÈÉÊËÌÍÎÏÈ ÔÕÖרÙÚÛÖVßàáâãäåæáWá[WWIVëíïìîðîç÷èáVûýþñóоƒéÀYûW�!5‡~ 3O\Âo 36\(ñ‡AXò)ÜHò!ÇŽ�¼ˆÄX’`ËŽß®²�e%Ä›ÂÌòbC‹úØú�è¢0Í]´’…ÚϦ>™:•5i¹¥V³^eǵ«×¯`Êõj!!ù,$2õ€‚ƒTQ†‡ˆ‰Š‹‹Mƒ��CPN”•–—˜™šT��Jš ¡¡PC�ƒI¢©ª•J¦T«±©I�’²· œ�Q¸½˜PƒM¾Ä–Q‚MÊËÌÍÎÏÐÑÊ*KÖרÙÚÛÜÝØ)HáâãäåæçèãEãEERHíïñîðòðéùêãKLLKHúý è @€ú®‡-à5‡Ö "LÎH=r Ã5Üø°!Åp)MÙǰ£Éˆ?>zBRãIƒ/?†+"ÈÁ‘–srŒ(óˆ%F0òsg�h@£KŒÊD—ÉkAŸu •êÔ¥çšbÝšÕ�ׯ`ÊK¬ƒ@!ù,$2ô€‚ƒ =†‡ˆ‰Š‹‹ ƒ��)?6”•–—˜™š ��7š ¡¡?)�ƒ¢©ª•7¦ «±©�’²· œ�=¸½˜?ƒ ¾Ä–=‚ ÊËÌÍÎÏÐÑÊÖרÙÚÛÜÝØ;áâãäåæçèã<ã.<< ;íïñîðòðéùêã ;úý è @€ú®‡-à5‡Ö "LNG=r Ã5Üø°!ÅpáØÇ°£Éˆ?>BRãIƒ/?†ã�,GK‰89F”™ÃÇù¹384`ÑEe¢Ë¸#‡5 N6}:UªÒsL¯jÅꮫׯ`ÊûÕA !ù,$2ô€‚ƒ-†‡ˆ‰Š‹‹ƒ��42”•–—˜™š-��!š ¡¡4�ƒ3¢©ª•!¦-«±©3�’²· œ�¸½˜4ƒ¾Ä–‚ÊËÌÍÎÏÐÑÊ'+ÖרÙÚÛÜÝØ,áâãäåæçèã&ã5&&0,íïñîðòðéùêã+11+,úý è @€ú®‡-à5‡Ö "LÎE=r Ã5Üø°!Åp½ØÇ°£Éˆ?>ARãIƒ/?†3�ÌCK‰89F”é¡À ù¹384`ÑEe¢Ëȃ5 N6}:UªÒsL¯jÅꮫׯ`Êûu@ !ù,$2ô€‚ƒ †‡ˆ‰Š‹‹ƒ��!”•–—˜™š��š ¡¡�ƒ¢©ª•¦«±©�’²· œ� ¸½˜ƒ¾Ä– ‚ÊËÌÍÎÏÐÑÊÖרÙÚÛÜÝØáâãäåæçèã#ã##íïñîðòðéùêãúý è @€ú®‡-à5‡Ö "LnB=r Ã5Üø°!Åp‰ØÇ°£Éˆ?>ú@RãIƒ/?†�lCK‰89F”¹!C… ù¹384`Ñ Ee¢Ë¨aƒ5 N6}:UªÒsL¯jÅꮫׯ`ÊûUB !ù,$2ò€‚ƒ†‡ˆ‰Š‹‹ƒ�� ”•–—˜™š�� š ¡¡ �ƒ ¢©ª• ¦«±© �’²· œ�¸½˜ �¾Ã–ƒÈÉÊËÌÍÎÏÈÔÕÖרÙÚÛÖ ßàáâãäåæáá  ëíïìîðîç÷èá ûýþñóоƒéÀYûW�!5‡~ƒ0O\Âo 36\(ñÛ€Aò)ÜHò!ÇŽ�ˆÄX’`ˎߤ :°¢M�aXð‚E}ìý7ôÁP˜æ.&8@Í'ÓžK›F…Š´œÒªX­²Ûʵ«×¯`Ãv•!ù,$2ô€‚ƒ)†‡ˆ‰Š‹‹%ƒ��”•–—˜™š)�� š ¡¡�ƒ*¢©ª• ¦)«±©*�’²· œ�¸½˜$ƒ%¾Ä–‚$%ÊËÌÍÎÏÐÑÊ'ÖרÙÚÛÜÝØ áâãäåæçèãã( íïñîðòðéùêã'))' úý è @€ú®‡-à5‡Ö "LnA=r Ã5Üø°!ÅpEØÇ°£Éˆ?>RARãIƒ/?†»€¬BK‰89F”YÁĉù¹384`ÑEe¢Ë¨ ‚5 N6}:UªÒsL¯jÅꮫׯ`Êû•D !ù,$2󀂃†‡ˆ‰Š‹‹)ƒ��$D”•–—˜™š��%š ¡¡$�ƒ¢©ª•%¦«±©�’²· œ�¸½˜�)¾Ã–ƒ)ÈÉÊËÌÍÎÏÈBÔÕÖרÙÚÛÖ$%ßàáâãäåæáá)%ëíïìîðîç÷èáBB%ûýþñóоƒéÀYûW�!5‡~;1O\Âo 36\(ñ[)Aò)ÜHò!ÇŽ�†ˆÄX’`ËŽß ‚`%Ä›ÂD`@È ‹úØú�¨¢0Í],�€ÚϦ>™:•5i¹¥V³^eǵ«×¯`Êõ$!ù,$2Ó€d‚ƒ$†‡ˆ‰Š‹‹ƒ��A“”•–—˜™$��š ¡™A�ƒ¢¨©¥d$©¯¢Ž‘°´ œ�Ÿµº—�§»¿”«‚ÄÅÆÇÈÉÊËĤ$ÏÐÑÒÓÔÕÖÑAŒÚÛÜÜcˆccáãåâäæäÝí߇ÑñóÐíîˆôÏòõñö†é½3”¯PA†H €hà>}ûùëÕ¿‹ë!3H ÅŒ jDøŒaExâ ¦”·’ÄJ„Úî3Y’ š7aÆÔɳ›¸Ÿ@ƒ J´¨P2�!ù,$2ô€‚ƒ†‡ˆ‰Š‹‹)ƒ��$D”•–—˜™š��%š ¡¡$�ƒ¢©ª•%¦«±©�’²· œ�¸½˜¤ƒ)¾Ã–‚$)ÉÊËÌÍÎÏÐÉBÕÖרÙÚÛÜ×$%àáâãäåæçââ)%ìîðíïñïèøéâBB%üüì÷/à¿|Õ…»ÐZÃj"w‚Þ8…àjtÈp"¸R‚è[ȱ$ÄŽ ™ÑdA—Á ˈ87BŒ‰À€�÷µ+8`Q!EcžÃXA5 N6}:UªRsL¯jÅÚ®«×¯`Êû5H !ù,$2󀂃)†‡ˆ‰Š‹‹%ƒ��”•–—˜™š)�� š ¡¡�ƒ*¢©ª• ¦)«±©*�’²· œ�¸½˜�%¾Ã–ƒ%ÈÉÊËÌÍÎÏÈ'ÔÕÖרÙÚÛÖ ßàáâãäåæáá( ëíïìîðîç÷èá'))' ûýþñóоƒéÀYûW�!5‡~[0O\Âo 36\(ñ›„Aò)ÜHò!ÇŽ�TˆÄX’`ËŽß."Qa%Ä›¬`âÄ‹úØú�è ¢0Í]TP�ÚϦ>™:•5i¹¥V³^eǵ«×¯`ÊõJ"!ù,$2õ€‚ƒ†‡ˆ‰Š‹‹ƒ�� ”•–—˜™š�� š ¡¡ �ƒ ¢©ª• ¦«±© �’²· œ�¸½˜ ƒ¾Ä–‚ÊËÌÍÎÏÐÑÊÖרÙÚÛÜÝØ áâãäåæçèãã  íïñîðòðéùêã úý è @€ú®‡-à5‡Ö "LB=r Ã5Üø°!ÅpØÇ°£Éˆ?>@RãIƒ/?†£6AÂ�–srŒ(óÀ‚0òsg�h@£ŒÊD—1ÁkAŸu •êÔ¥çšbÝšÕ�ׯ`ÊK¬„@!ù,$2ò€‚ƒ †‡ˆ‰Š‹‹ƒ��!”•–—˜™š��š ¡¡�ƒ¢©ª•¦«±©�’²· œ� ¸½˜�¾Ã– ƒÈÉÊËÌÍÎÏÈÔÕÖרÙÚÛÖßàáâãäåæá#á##ëíïìîðîç÷èáûýþñóоƒéÀYûW�!5‡~›0O\Âo 36\(ñ›…A"ò)ÜHò!ÇŽ�>ˆÄX’`ËŽßF Ú°¢M�anÈPa‚E}ìý7´ÂP˜æ.jØ@Í'ÓžK›F…Š´œÒªX­²Ûʵ«×¯`Ãv!ù,$2ô€‚ƒ-†‡ˆ‰Š‹‹ƒ��42”•–—˜™š-��!š ¡¡4�ƒ3¢©ª•!¦-«±©3�’²· œ�¸½˜4ƒ¾Ä–‚ÊËÌÍÎÏÐÑÊ'+ÖרÙÚÛÜÝØ,áâãäåæçèã&ã5&&0,íïñîðòðéùêã+11+,úý è @€ú®‡-à5‡Ö "LÎE=r Ã5Üø°!Åp½ØÇ°£Éˆ?>ARãIƒ/?†3�ÌCK‰89F”é¡À ù¹384`ÑEe¢Ëȃ5 N6}:UªÒsL¯jÅꮫׯ`Êûu@ !ù,$2ô€‚ƒ =†‡ˆ‰Š‹‹ ƒ��)?6”•–—˜™š ��7š ¡¡?)�ƒ¢©ª•7¦ «±©�’²· œ�=¸½˜?ƒ ¾Ä–=‚ ÊËÌÍÎÏÐÑÊÖרÙÚÛÜÝØ;áâãäåæçèã<ã.<< ;íïñîðòðéùêã ;úý è @€ú®‡-à5‡Ö "LNG=r Ã5Üø°!ÅpáØÇ°£Éˆ?>BRãIƒ/?†ã�,GK‰89F”™ÃÇù¹384`ÑEe¢Ë¸#‡5 N6}:UªÒsL¯jÅꮫׯ`ÊûÕA !ù,$2ô€‚ƒTQ†‡ˆ‰Š‹‹Mƒ��CPN”•–—˜™šT��Jš ¡¡PC�ƒI¢©ª•J¦T«±©I�’²· œ�Q¸½˜PƒM¾Ä–Q‚MÊËÌÍÎÏÐÑÊ*KÖרÙÚÛÜÝØ)HáâãäåæçèãEãEERHíïñîðòðéùêãKLLKHúý è @€ú®‡-à5‡Ö "LÎH=r Ã5Üø°!Åp)MÙǰ£Éˆ?>zBRãIƒ/?†+2èHK‰89F”yÄ#ù¹384`Ñ%Ee¢Ëˆäˆ5 N6}:UªÒsL¯jÅꮫׯ`Êû@ !ù,$2õ€‚ƒZ]†‡ˆ‰Š‹‹Iƒ��'\_”•–—˜™šZ��`š ¡¡\'�ƒU¢©ª•`¦Z«±©U�’²· œ�]¸½˜\bƒI¾Ä–]‚bIÊËÌÍÎÏÐÑÊ ÖרÙÚÛÜÝØVáâãäåæçèãWã[WWIVíïñîðòðéùêãVúý è @€ú®‡-à5‡Ö "L.L=r Ã5Üø°!ÅpaÙǰ£Éˆ?>òBRãIƒ/?†»"ÈB––srŒ(3Ë‹ a0òsg�h@£ŒÊD—ÑJkAŸu •êÔ¥çšbÝšÕ�ׯ`ÊK¬…@!ù,$2󀂃nP†‡ˆ‰Š‹‹pƒ��lr”•–—˜™šn��fš ¡¡l�ƒo¢©ª•f¦n«±©o�’²· œ�P¸½˜l�p¾Ã–PƒpÈÉÊËÌÍÎÏÈmÔÕÖרÙÚÛÖCeßàáâãäåæá á; heëíïìîðîç÷èámiimeûýþñóоƒéÀYûW�!5‡~›3O\Âo 36\(ñÛ�Agò)ÜHò!ÇŽ�ÔˆÄX’`ˎߺ°f%Ä›Â\§Í‹úØú�h¢0Í],³†ÚϦ>™:•5i¹¥V³^eǵ«×¯`Êõz!;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-gray.svg0000664000175000017500000000540015257164523031670 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-green.svg0000664000175000017500000000542415257164523032034 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-red.svg0000664000175000017500000000540015257164523031500 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/spinner.gif0000664000175000017500000000302315257164523031045 0ustar00zuulzuulGIF89aõÜÚÜtvtÜÞÜTVT´¶´¼º¼  ôòôüþü„‚„üúüôöôìê윚œÔÒÔ”–”ljlŒŠŒ¼¾¼<><,*,|z|\^\\Z\¤¢¤ÌÊÌäæäDBDäâälnl¤¦¤dfdÄÆÄLJLDFD´²´ìîì$&$¬®¬œžœ $"$”’”<:<¬ª¬464TRT„†„,.,dbdÌÎÌÔÖÔ|~|ŒŽŒÄÂÄ424LNL!ÿ NETSCAPE2.0!ù ,•À€p8àˆÈ¤pU(¬”ÄgHj’„‰#­.BGÓÀ©ÆDNÓ2Ä¡fA„¼ bJN§ ¢@¡!oP#'B # „"s�r3–„˜Mš  �7�’�H'‡©B (3ª1ŒI r5\C¸xI(rD7ˆI>r¤ B5‘&�&ԉ̭PA!ù ,”@�p(d0ˆÈ¤`ãzl”Ð@¡rä öŒ\®‘€Ñ*¨²Cuh¦qh(¤B‹½N}� Cƒ�'$*$'&&†/p/���‘S/…�ˆŠ€†DG B"› P&pUC!«^b"p(D'! ,S,—P+p+ (|J#Êb£ *, PA!ù ,’@€pXØD ¢r9*FÂÅ¡Ä]NÑ#C8+´°ÃK�°,œÐ!‚�(hRÛ]NÒïøe‚&£%%xNO‚wMhyQ|4v�J'‘D7D$…4KNd *…T…6C…" 0N0 7…C*$ …‚6˜¦B £T�#%%iKA!ù ,�@€p8L¥ˆÈ¤páb%”Dı8ZºdJÕ€Ú 9eг™ h%n…K'C…xÈM±Qµx¡–n° , yq|h~*'prn"wkmnJ):P 8H/q*˜H %`DzY7B8z/D zG:qY9ˆ¡B / Lq.O¼P$q%”D ::'PA!ù ,“@€p8ˆHÀ§Ô1„O’è)XBã°!;ÖÂ`*ˆˆ¢phº}ˆ Å`=M.‹úT€* Žz a �b†Vˆ�‹|~€zwO‰I˜zœC~™I $yBa&B § ¥^B)VGB$'D)ÁV$B #Åz W�C )& SA!ù ,‘@ƒp(ÔX,¢R )8 �¥pAÄ ‘gÁ!5О£æ3ºX5ˆ³ç˰:®„‚-1æ<º€…rôºw—z€�‚ !O!‚,‹N�‚* .†Ž‰B žR £C'5])sO¦K¢B,‰22B)J!'BNC¸N§p~KA!ù ,�À€p(Hl¢R¹è -†�³Pò&IÜó‰ô Ã�÷|œÎBg1Û>ª�É–Ø$€@k[à=pD [ C'RK 3IK““+$+”“[8™B808Z\ž �–˜¥«¬­D 6” +~C£”+6D½B ;ˆ6t¿8Çdˬ“A!ù ,ŽÀŽp(dh4 ¢rÙÁ &‘cH&ÏÇbq± SleÑÉ<£‘ga@4¨“FdG¢¶•B€IPï‰ RdƒR B‰ j XIƒj—™‰œO�‘‰”O ’J�­’ƒˆDRKC¨ `‰ Z²DƒA;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-gray.gif0000664000175000017500000003207415257164523031444 0ustar00zuulzuulGIF89a\\÷úûûüüüûûûøøøööö÷÷÷þþþõõõúúúùùùýýýáââÙÙÙÛÛÛçèèíîîîïïñññóóóäääëëëêêêçççðððéêêåååàààãããèèèììììííòòòôôôïïïíííö÷÷ééé÷øøÐÐÐÞßßäååÒÓÓâããÛÜÜâââóôôÝÞÞîîîØÙÙ©©©ÏÏÏÏÐЮ¯¯àááßßßÜÜÜñòòÂÃÃÚÚÚÄÅÅÊËËØØØÓÔÔÆÇÇÉÊʹºº½¾¾¾¾¾ÔÔÔŒ��ÖÖÖÅÅ۱±¾¿¿ËÌ̸¸¸¨©©¡££”••­®®³´´ÐÑÑÜÝÝÍÎÎÆÆÆÖ×׿ççßààÑÒÒåææ½½½¶··³µµÎÏÏ•––ÃÄÄÔÕÕÙÚÚ©ªªÑÑÑÊÊÊœžž»¼¼¿ÀÀ±³³Ÿ  ÝÝݦ§§ðññÀÁÁïððãääÈÉÉ·¸¸²³³ÌÍÍÅÆÆÞÞÞüýý¼½½ÓÓÓêëëôõõÕÕÕæææÌÌÌÿÿÿ‚„„ÍÍͧ¨¨™››º»»‹ŒŒ ¢¢±²²¬­­ ¡¡¦¨¨­­­!ÿ NETSCAPE2.0!ÿ XMP DataXMPB?xpacke2CCCBD3" xmpMM:InstanceID="xmp.iid:7470F0FCE32511E2BE7"r"?>!ù2,\\ÿH° Áƒ*\Ȱ¡Ã‡#JœHb–‹/v¨È±ãÂ,)Ú@D²¤É’PÚ¤Øè±eÄ ‚ :I³fIDA$pÉaL›$AJJ" ‚‚\éÙ3Áœ™'qÊòÁ�Õ«Xýx¸h$ÍD)v2­èjÉD?ai˜�É6¨o _Kú&o¦¾K2μ- ›…‘ßìÛ¼:Vf£»L®—ºõïZÍfÿqyålUðèÓDtœ¢ìõÞÓ÷l‰ â"Š/ÿ{–’OpÔIW‘¤g§"Á$–d,²'-›,RœAebA$þ@¬©\ˆIÙZµmIÝúñ-´›‘8¤AqUúØ©X•–åUî¶ÛëfÉA1Ú¬º’êªV'í¤IþšÊÙ¿‚è°×š:TM ÓÔð¼ºB9fI¥ÆëÅänæÕ´Qla¾‡LRjæäÕrœqªòÊ,•š“ç´qjÕlóÍ8ç¬s¸Ž…š”É%Z,òЂ¨ëX ö×—m™4ÈE‚=˜ RG 5ÑEs†tAøæ Ha<÷uØ‚Œ-6ØNm4 $%=P×ë–ÈÜbÓ]¶Ýt§}ê!W›ÿ´6`m´´Ð$å}÷܇×�x¬hë·Ö�4s‰Wn¸á±ÊK)䂸-Ðä^ã-:â˜ÃZé…<¾™ÏA-Ê¡“>ºâz›ZÈ€rÒßΑsÇ…^öEb?|Å#�5ïl•LÅ…æ[ÈܺOŸ» ÖWOýò)£IÐÁÂcÍ+ókáYPpªóúì·Ï~|Pº)·lÿýD1�U½UK¹ø»"VœW�ä ,nßýRG^EZÁ• ò¤§š¸ï‚Ñ]Íô)V‘d_Ž ÆèÙÊ >KÈP¢À–1pw�Q•¤6B bƒ„á_’,… �"”•ÿ©„j‚5L` WöBÕµåˆKˆšØTÁÞð‚9t"[êÔ/U1ˆ±"ߙХ¶ýe‰hÔÓòS«‡vý‰ãUÒE­'8rìÁ~×� (�q”�$R!@Ê'YƒÈ€Q C~‡;|„HÔ´GVçˆâÈ{ºcÉáÐçŽc"ô×ɶ|R/í©w*YÊÍœ’ŒÏ¨a*dËVºR°XsŸ ù P„‚©¼ÐD)åLd‚±½è$¢<‰HJ…ˆ”¬£èÄ™3AJÒ’š”¤!ù,2/ÿ`ƒÁƒ*\¸0Á‡‘™H±¢Å‹16�qJÆ� A.á8�DÈ“()!  IÊ— �8„¸¦ÍŒ¹ÉóₜôJqÅ@—D‰:Ȱ©Ó§M™JµªÕ«X³j­Ú²‰×¯`ÊK¶ì×®fÓª]»‘­Û·`ѦM"Á‘°tíâ­{W­\³ >Xà௅6û·,Á qB>+Ù/[#„Å @3XΞÓ6.;uli±§-Ã]½v4ë×g·ÊžM›*¸sëÞÍ»·ïߺÀ.ÖØ$¸ï"w þzùñÞåq.6zXôæÅÇ'/îCÅÐßß/¾ý�‘å'ôHÑ0–{ó5ŸrÑÐA�Å ¶q—áubˆAzÂ1HƒxŽH¡&–Hâw౜o0Æ(ão³Õh#V ¹¨ƒcÝh£‚eñØÞŒDÞVa�:¶&dX>Ö¤…g)å‘P&¹ãYMÎöä‚QJI$•\Z™Y–ÉU!ù,2/ÿ˜À�Áƒ*\¸PÂÀ‡ IC±¢Å‹3fD“ "D#CŠùÅã@#Sª¬ØÀd4+cŠ\ã0â™85¢‰Ø §OŒ@JXó³hÅa5º¦cA†P£JeèðˆÕ«X³jÝʵ+VhŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷-™º�½›w/^½mã²E0ÐÌX ‹E @qZÁ�Q+™²ÀÉl!£U#� YÎ<�-:³\«fQ—U˜®ë·š_ËVëµ¶íÛWtØÍ»·ïßÀƒ ï�`¶ñ²Æ’Ù­wy‡æÌÑ8|Mr±0X°€�&ûöîÚ¹{¬ç^ÝúXèŧï¾>=y¹g¢�½Ö={ô÷ÕãŸ{ ðüó툟}s=„YXôå§ }ÊÕ�@lñvêg!][Ãd%Ãnâ�Ø�ˆvðÞq n�‡*nˆF‹,®Xžy37Ü�8æHn<öˆUi5¾–àX>ö(!rgé¨do"¤lCŠU$�Gv˜ä’K6iå“Bž5%nUNˆ–Jj)&—suðåš\±!ù,2/ÿˆƒÁƒ*\¸ÄÀ‡ìˆA±¢Å‹3f ‚ "DCŠ ÄãÀ#Sª¬hÃ$‚ +cФá0"™85‰h#§OŒ2‚ ñ³hEa5J£cA†P£Jeè�‡Õ«X³jÝʵ+VAŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷í |�½›w/^½mã²}˜CíÀÂb ~û�‡a�ŽŒœVpÚq�Å @óXΞ˵j–tYÓ‹éªvkyµk²¼ÊžMû*€¸sëÞÍ»·ïߺ_¾NöÅØ¸õ"¡dÁÇ™§[xr"!’— çqˆâ†X¬¨bŠäý£lÂÕhã�ÃݦãŽX‘6£k ŽÅ㎖¤X8&ɇFþHãYCêXd‡f)i%“T:ÙÚ‘aEyÛ”Vie’X†©%] x©&W!ù,2/ÿ …Áƒ*\¸0Á‡O˜˜H±¢Å‹1€±BÆ� A²à8�@È“()¾ FÊ— S8„Ȧ͌¾¸Éóâ‚ RôJñÂ@—D‰¦Ȱ©Ó§Mª˜JµªÕ«X³j­Ú†×¯`ÊK¶ì×®fÓª]»‘­Û·`ѦE�€…°tíâ­{W­\³]Ä(ø,a¿l ª,p±áƈ×B8 ìd•Á^Μö¯Ù©cA‹®éÒ§S�°µµë×T˜M»¶íÛ¸së®íYµo`Q̾+œñá0Š÷õíø×.t€]:õèÓ«OgÞl‡ïÙÁS ~»[ ÈÁ:÷Z~üw÷áß¿­+°p÷çäó¿oÿV±zïúÅ' \ ´Âøí |p­@B„µu³eW!…X˜!w÷yµÂw~! "†"‡0Lˆâi»µè⋼Á&ãŒUq¶"‹dÑ8ã�b©øŒ@Öa�7ªæ£W:ÊÈ£„déä�L‰chI¾¶$‚=: $”XJùU†™Õ!ù,2/ÿ€€Áƒ*\¸Á‡30˜H±¢Å‹1j€BÆ� AZà8p@È“()F @CÊ— 8„h¦ÍŒF¸Éó"‚ôJQÂ@—D‰6Ȱ©Ó§M:˜JµªÕ«X³j­ÚRƒ×¯`ÊK¶ì×®fÓª]»‘­Û·`Ѧ­ @…°tíâ­{W­\³ NXà௅6û·,AqB>+Ù/Û•„Ŭ,çµ�ËN;ZliËpSƒVÍšñÖ×°cSP ¶íÛ¸sëÞÍû¶‚ÖÀÅ[¡öÝâŽ×€¼op ÿ^� ႆéÕ¯S·ŽÝúsè`/ˆ¡ç>þzùñÞÝfX6ºWôæÅÇ'/ÿ­Áíßß/þÛÇùIÇ}ÂU€@ ,à{Îçà[ (ä”EVéå•s%°å™\ !ù,2/ÿh�Áƒ*\¸PÁÀ‡|è@±¢Å‹3f a "ÄCŠ)ÁãÀ#Sª¬8À¤�+cŠ|à0¢™85†ˆ8 §OŒ *xð³hÅa5ú cA†P£JeèP‚Õ«X³jÝʵ+V!ŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷-�½›w/^½mã²í(ÇX ‹E¬8­`Ç%¨�}:3×éâV ;w�Ý{i°]@@€ŠOn¹rç¹@�yõâ׫'o üëôìà©cŒßvæ‚ÁÒ½jo]üú¶‰»~w¾ýv¶ ËW_Ÿ½ÿ¶ Àcûu%€AÌ!Xœ‚*pŸpÄ @cX@…N]zBÛ‡ æÇÚˆ$NE\‡£�ØU‰$¢ç•ŠÒ…�/¢HŒ¿±Èš‹‚%ãŒaáhcZ8ê¸c�býø�=é–FFy•!ù,2/PýH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç� CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ§ÏŸ@ƒ J”`@!ù ,2/ÿh@�Áƒ*\¸ÐÀÀ‡$8@±¢Å‹3f,à0âCCŠ™Àã@#Sª¬(À¤�+cŠ$ÐâD™87F™³çÅ’ øZ1À@˜D‰Ò\P�¡Ó§P:L@µªÕ«X³jÝjuA�¯`ÊK¶¬Ù°^Ϫ]ËökÚ¶pãºm›À€bëÞÍkïÚ·jk+˜ðÀÁjŸ}˜íÀÆ`ÿmkt�±•/‡ÍÌVñYªdA�=Y®éΧS/æÊºµëª–£Êžíô¥êÛb5M`/o¾{øíwÝ ùòæÊ™;—nüxXØ¡gO¾={ó¸Äs_’ïN;wóq ƒEîý¼vóíãJ^?~ù÷ßá*hñWö÷¹'`\ dô�%€AÐ1˜œƒ *�nÈ €]cX@†^X�uÞFÛˆ$dÀk(¦hr!šÆ"X*¦è_X/þWâlÒØ¢j5ãk3&8Ö�8’ÕãŽkõø#�F–E¤l9 ‰d\ ,i¥V !ù,2/ÿ�Áƒ*\¸PÁ‡?t˜H±¢Å‹1†€±@Æ� AJà80@È“() DÊ— 8„(¦ÍŒ¸Éó"‚ ôJ1Á@—D‰>Ȱ©Ó§MJ˜JµªÕ«X³j­Ú2„ׯ`ÊK¶ì×®fÓª]»‘­Û·`Ѧáð@Xºì‚Å«7­\³qÄ(ø,aµË”0X cÃŽ³] ÀNXÊ–Áb^›¸ìÔ±ŸÅ†– ·4gÓ¨o]ͺ5UbËžM»¶íÛ¸gwN�ZÀÞØvA!\@ðἿúþ: A‚!š?�îºtèɽ.÷: »uïÑÁ{œÇîVråšÅ«ïžýÛ™ kOÏ~|ûïîÝ.»ý¾ûö½%À@lðG~ú÷# ðzÌÅf�„ÑQ8…Ù…ÐÝ1Æ¡ƒ!|èa‡jXbr¹¥¨âŠº¹æâ‹Uev"jý�㋊U£r,ö(ÛƒÎH#Y7º˜cX;jç£�@B(diI†P¤kG©ã’=69ß“PNéeVv!ù,2/ÿ¨ „Áƒ*\¸0ÀÀ‡tØ@±¢Å‹3fä  "ÄCŠ)Âã@#Sª¬¤+cŠœà0¢™85rˆ"§OŒ!˜ð³hEa5:¡cA†P£JeèP„Õ«X³jÝʵ+VŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷mˆ "�½›w/^½mã²í(�ÂX ‹E¬8­`ÇE¨�ÊfdXBæH$‡f%iå’T6ÙÚ“DyÛ”i%’X†©å\ x©&W!ù,2/ÿ€€Áƒ*\¸ÁÀ‡d`@±¢Å‹3fÔ "DCŠiÁãÀ#Sª¬Ád +cŠlà0¢™85jˆ!§OŒ"hð³hE a5Ú cA†P£JeèÐ�Õ«X³jÝʵ+VŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷m(�½›w/^½mã²}8AíÀÂb ~ûÐ�a�ŽŒœVpÚ–�Ŭy,g·–ÓZ5;ºléÅtSƒVͺ²××°c_P ¶íÛ¸sëÞÍûvèÖÀ Œ­P[/ñÆ‹k8xXáb/H�pAƒtêÖ§W¿^Ýùó±Âo£o�¼øîr3( =ìùòáá��?¯@Äߣ›ßÿýÜÆì�Çß|ÒUÀ@ ¨_ÊG×\�YíYWÛvZX†z—_X „7aˆj@âˆ"z¨A…*ªÖÛ‹0Æè›l4ÖˆUg-²Æ¢X6Ö˜`Y;>'ã�¶MdŽ­©A�4þHáYDFiä“Hºx“²9© �Q9å–UÎU–dr•@@!ù,2/ÿ …Áƒ*\¸0Á‡O˜˜H±¢Å‹1€±BÆ� A²à8�@È“()¾ FÊ— S8„Ȧ͌¾¸Éóâ‚ RôJñÂ@—D‰¦Ȱ©Ó§Mª˜JµªÕ«X³j­Ú†×¯`ÊK¶ì×®fÓª]»‘­Û·`ѦE� €…°tíâ­{W­\³ºXà௅6û·,AqB>+Ù/[„Å @3XΞÓ6.;uli±§-Ã]½v4ë×g·ÊžM›*¸sëÞÍ»·ïߺÀ.–X¸ï"' <9Œå}‰Ã0þµÃ… `XÇ®ýzöíÙ¥O¥Û¡üwóÚÑ›ïVƒs°Ô½®O_žþùúo†O^½ÿúó½õÕýwß�p P+(Ÿ�öEøÖ $t AXñi‡ÛwjH‡Š7ÞW+”wa‰€â‰&Š˜¡ˆ¬'ãŒ4WÛ�8VŒ1’•#Ž GV�Dêv¡�<¾öâW?Þ$†CYä‘P&Ù£iMÒö¤ƒJI$•\ZéY–™Õ!ù,2/ÿ ‚Áƒ*\¸pÁ‡aP™H±¢Å‹1b�ÑJÆ� A®à8ð@È“()V KÊ— y8„¸¦ÍŒV¸Éó¢ ‚xôJñÁ@—D‰òȰ©Ó§MJ™JµªÕ«X³j­Ú‹×¯`ÊK¶ì×®fÓª]»‘­Û·`Ѧ½’‹°tíâ­{W­\³«Ä(ø,a¿l J,p±áƈ×RH ìd•Á^Μö¯Ù©cA‹®éÒ§S�°µµë×T€˜M»¶íÛ¸së®íYµo`¯Ì¾+ñáXŠ÷õíøW/^pÀ]:õèÓ«OgÞ,‡ïÙÁS ~»[ÈÁ:÷Z~üw÷áß¿­+°p÷çäó¿oÿV±zïúÅ' \ äÃøí |pù� „µu³eW!… X˜!w÷yåÃw~!"†"‡XLˆâi»µè⋼Á&ãŒUq¶"‹dÑ8ã�b©øŒ@Öa�7ªæ£W:ÊÈ£„déä�L‰chI¾¶$‚=: $”XJùU†™!ù,2/ÿ(‚Áƒ*\¸°ÀÀ‡Œ¹C±¢Å‹3f„# "DCйÇã@ #Sª¬ÈǤ8+cŠlã0â™85‰È'§OŒahó³hÅ<a5Ú¦cA†P£JeèЈիX³jÝʵ+VpŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷­ŽÔ�½›w/^½mã²í(pÆX ‹E¬8­`Ç�¨�=y¹A¢�½Ö={ô÷Õ㟠¢ðüó툟}s=„YXôå§ }Ê…�@ Ððvêg!]4tFd%Ænâ�Ø�ˆbðÞq Ò€‡*n(F‹,®Xžy37Ü�8æHn<öˆUi5¾–àX>ö(!rgé¨do"¤lCŠU$�Gv˜ä’K6iå“Bž5%nUNˆ–Jj)&—saðåš\…!ù,2/ÿX`ƒÁƒ*\¸0ÂÀ‡ )B±¢Å‹3flR "Ä)CйÄã@#Sª¬HÄd�&+cŠtâ0â™856‰H$§OŒCFpò³hÅa5ê¤cA†P£Jeè0ˆÕ«X³jÝʵ+VMŠK¶¬Ù³hÇ‚M˶­Û°kßÊ� ÷m�GÈÞ�wìÞ¾lãøD-aà ~û0bŽÅ6n+8­�È^�yìæÎ‹ßZ5;ºliÊtS3VÍ:-d¯°c˶ À�íÛ¸sëÞÍ»7îÊ­ƒ7qà×vÞ$Æ› wp<¹ð°ÄÅF‘"%J“éÕ¯S·ŽÝúóác£ˆ¥ç>þzùñÞåBq.6zXôæÅÇ'/îCÅÐßß/¾ý�‘å'ôH—11–{ó5Ÿr1¡DP�Å`¶q—áubè@zÂ1È„xŽHa&–Hâw౜o0Æ(ão³Õh#V ¹¨ƒcÝh£‚eñØÞŒDÞVa�:¶&dX>Ö¤…g)å‘P&¹ãYMÎöä‚QJI$•\Z™šY–ÉÕ!ù,2/ÿ@ƒÁƒ*\¸ðÁ‡Åü™H±¢Å‹1 ‚NÆ� Aâ8�CÈ“()Ž! @PÊ— 9„h¦ÍŒ޹Éób‚õJÆ@—D‰Ȱ©Ó§MB™JµªÕ«X³j­ÚR�ׯ`ÊK¶ì×®fÓª]»‘­Û·`Ѧ$a@›°tíâ­{W­\³Xà௅6û·,A(qB>+Ù/Û=„Å @3XΞÓ6.;uli±§-Ã]½v4ë×g·ÊžM›*€,¸sëÞÍ»·ïߺÀ.6 ØA¸ï"Ï¢<¹ å}‰ 2þP˜0€YÇ®ýzöíÙ¥O¥ ¨üwóÚÑ›ïö�s°Ô½®O_žþùúo%†O^½ÿúó½õÕýwß�pe!Ð…(Ÿ�öEøV!?rHXñi‡Ûwj˜‡Š7ÞW…”wa‰ ‚â‰&Š˜¡ˆ¬'ãŒ4WÛ�8VŒ1’•#Ž GV�Dêv¡�<¾öâW?Þ$†CYä‘P&Ù£iMÒö¤ƒJI$•\ZéVY–™•;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-gray.svg0000664000175000017500000001075215257164523031475 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.514496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-green.svg0000664000175000017500000001143615257164523031633 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-red.svg0000664000175000017500000001323315257164523031302 0ustar00zuulzuul Compute Compute Load Balancer ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-gray.gif0000664000175000017500000002345215257164523032036 0ustar00zuulzuulGIF89a\\÷¼¾¾ÙÚÚÐÐа²²ÆÇǰ±±¡££¬­­âã㸹¹³´´Áª¬¬õõõñññ¯°°ÝÞÞìì즧§çèèËËËúúú•——ßàà­­­”••™››ÙÙÙ‘’’Ž��¦¨¨ÍÍÍ‚„„ìííÒÓÓ ¡¡ÀÁÁ¹ººˆ‰‰åææòòò–˜˜ùùùéééõööæææÌÍÍÁÁÁÖÖÖïïïÇÇÇÏÐÐÃÄÄÉÊÊòóóÈÉÉÊËËÜÝÝÓÓÓüüüôôôîîîãääáââãããååå÷÷÷êëëäååàààýýý€‚‚ˆŠŠrttÇÈÈcee�’’¨©©{}}˜™™jlluww|~~y{{רØÿÿÿ˜ššççç¿ÀÀº»»½¾¾¼½½óóó±²²ËÌÌëëë´µµûûûÓÔÔñòòþþþðððôõõöööîïï···ØØØ¡¢¢ž  ¢¤¤ÂÃÜ��«­­£¤¤ÈÈÈ‘““ÑÑÑøøøØÙÙÕÖÖÜÜÜàááÞßßÔÕÕÛÜÜ×××½½½—˜˜·¸¸¤¥¥²³³�žžŸ  ’””ö÷÷óôôðññïð𾿿ëììÔÔÔÄÅŵ¶¶ÌÌÌ£££‰‹‹suu”––ƒ……›�����Íηˆˆª««éêꃄ„¢££yzz±±±†ˆˆŒŽŽ©ªª!ÿ NETSCAPE2.0!ÿ XMP DataXMPÿ?xpacket begin="ïid:805D6FD7EA7311E2A8569C25BE948F7D"> =<;:9876543210/.-,+*)('&%$#"!  !ù2,\\ÿH° Áƒ*\Ȱ¡Ã‡#JœHâ…‹/^©È±ãÂ+J Œ8B²¤I’HF$P²Ñ£ËˆBf4Ar²¦Í’H Ìò²'B™7I2A´èšA›PñéS¤&‘4!q!F•«X³V™@…ÄÈšL”ðdZÑ)T¡ &h]Ëë…LN"Á2–,D%gsªmË—ï…'™Ì°ëðŠ“LHé˸¯gGÄ ¬KT�33~ ‰Ê…|%™`±æÓ|c41Ù¤.å+P™\@M»/¨VZžµ€éÚÀ׊Ʃ›é “3‚+oKç`ã8÷.Ÿž•wÉç/�£”N½ûV¨^Rÿ)É„»÷î�")Nööóð¯¦GéZ¢�ÃG�˜�ßýBI+­Fü¨Ý Pt fIáÁ„Ô 6(ÄBÕÇ�€HüVa|”„…E×}Xas$MæÐWšha\GàÐ�û¹xÞx6„_6~ø•Œ ù—b� y„Š Ø‘bˆ B1”4[m3eå•XZ9åtÚ!¡�B×\•y�N@¡æšlªé*‡ÔRá§„˜3%×ÖS,á矀ú9ÅOÄ Üƒ@å�À‘@Bˆz®Åg ”.1Å#,9Ž^¤ÝÊ9Zo5NZ) —^ÈãtHEXЃ „ÿÊ ©’öyꟗV!„gÓ=8¢A0nY›¨WÑš•©·Z:ez‘§Ý¡•´±Å"AEFh¶©-N(�Qs5j"I ª¬Z Åì¶ëîN¤”%Q‰-W’Yv„¦b2ˆ I$»-òE-p_¹š/Iþ6Úp L0c׿kA_5;ìÃ';q_Ó†â¯%<-Ç{\0sÓæ¾C8íPF1‘ò­Ü2jBBKRájFBmFqó©9³¼\ÏI;íÑK$5¥I³2Ï$ù<�Ó¡B-µÄ+[½óiLcЙuüç×*S<¶fe—,s×�²�sØk]MvÖ1ÿ!ì°^OhÕy¿�ÙËMô­±È�ƒíö´$‘¼ð†7¦¶ŸI¥¹M•3v1Aúòۨו�…ÀItÞW™ûtÝ¥Ÿžlêö¦X�\�;c¦£®z[ãqP°tJûê²ßz¼³|k¨SH-ý±û®Üç}ªÜxS9êUõ³ÿÎV«-z¤rÆB ¾òâkÅiBvÎlšÞXõ~pˆ&ô2½ª>ïÉ;Õòh3§„˜ïoµÉÔÿ�g=*¡d!JšŽªèwû±8NJ�BŒd•å� HXŸÛw2dGÔIÍæ…'¼ë…R a~ÔQ'fÊ¡ËÔÁÓàÈo+Ê“ÿàóA’$Š!FºÓ»ƒ¢#A„C=\bp‚'¹†ìJˆR Î…2$‘eñPR¢ˆ€Ž@ /žæ@U„È}ÞcÆÆ @qO~ÐÖÆùäGCù¡~Ú<õ°§"Ú#³bG ö$�td’uH‚�C"g�MDB#}²ÈŠHÃAÉ}"Ç#ÈFŠ·ùÏ&™’IÒ\²Aªa  £/”(ÆDœÁÉg@sà æ•ü‰eI$CË/奉$ÛƒÈ3É^Ä,�I‹rÞ#œÐŘA¦\¦RÆpÅ+6 Ë*¡© e…F9KM”ÂMŽÄd&›Ó\NvRΞ€D$éLÉJFÙNŸè�žõ̧>÷YÏ€!ù,#$ÿ÷dH° ÁƒXÈ�€¥"Jœx É”‹3^DR©Ã…ºLÚó±$L’Rª\™$HÐd‰dÉ…'Yꔄ A¦. °g’¦›rî\Ù³¤2=jåR•=Ô™t äP©F«^Ū€¦‘„~,ʇ¢E�p‘\’èP“Ú� "aÚË·/&$“¬$ôkÃ#c%MÉ„4há’�]ÜXæÝÉ)WNû8¨ä«š+®d°Ò祡¦¤‘Òé�©‘~}äµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà–²kßν;wæÁ 7{Î4%ñð*ÏW'o}Jõ›Ù�==½x¤ò¯ÒÇT»?¦û7å·Ôy|XÒEad`Iî¤ÞTQÅWÞ|_БWëM¨Ÿa ÀÄY†í}ôÓ‡†È�S \6ž†ÞWG%“,("‹xi‚ 4 ~õÉ‚L$ä�DNä@@!ù,#$ÿ7(H° ÁƒOXÈðƒ'"Jœ(‚‰‹3^´àAÃ…JŒØð±ä‰ Rª\™2 ~¡�dÉ…'Yê‘á„‚?|°aDˆ›rî\ÙS2=jåR•=¨!äP©F«^Ū@ˆ‘„~,z�¢E�p-ˆ�è0„Ú�$RdØË·o #ô ôkÃcA˜P€4há’�]ÜXæÝÉ)WNû8¨ä«š+ö`ÐÃ祡æ ‘Ãé�©‘~ÝáµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà²kßν;wæÁ 7{V`"qð*ÏW'o}Jõ›Ù�==½x¤ò¯ÒÏP»†û7å·ÔyhPBa(`Iî¤ÞTQÅWÞ|_}БWëM¨Ÿa$ÀÄY†í}ôÓ‡†È�S$\6ž†Þ§‚#,("‹xi‚ 4‚~õ˜Â‚(L$ä�DN„B@!ù,#$ÿ!øH° ÁƒWXÈPŒ‡+"Jœh'Ο‹3^Œ#ˆaC7 x¹Â@¡“(Sž4ÐH1bÐx9raI•8 XáÇ�G˜ @SÀÍœ)wt£¡˜…B‰’4‰¥Rì, uèÔªV@×®>(ZÔÈ6Ž‰Ñœ]x#��»xóˆsaAA0Ÿþ¶Ð?E~IìáÄNçšm\õ1d³‹¶ 9° ƒ‚(#åœ80!�„Dç$]4°êAªq²®éZ%ì͈/×NyÛqîÎ0_ÇV9ûepÛÃSW,F8Ûö@½ºõëØ¯ç-²Ÿ?…mtG)ú÷ðãO–¿|¬xéÜ̓wÓìûº'n_U¼n&†úÅRåEå”|èmÕÑVÖt`NëpƒK˜1HŸG=eÈÞ|ýÈ”d‚ˆ`„‚áHâT!Cd}æ×@Ä¡×��¬ÈÃD<öèãD<!ù,#$ÿH° Áƒ#XXÈ0€"Jœ¡€„‹3^, Ã…($@ð±d„Rª\™òÁ‚p €dÉ…'Yê4ð  °€ �ƒ›rî\Ù³Â2=jåR•=-THäP©F«^ŪЂƒ‘„~,:�¢E�p @�èÐ�Ú�<ØË·ïƒ  ôkCc H €4há’�]ÜXæÝÉ)WNû8¨ä«š+`À祡> ñÀé�©‘~ÍàµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà ²kßν;wæÁ 7{& !1ð*ÏW'o}Jõ›Ù�==½x¤ò¯ÒP»ÿƒû7å·Ôy(@Aa`Iî¤ÞTQÅWÞ|_БWëM¨ŸaÀÄY†í}ôÓ‡†È�S\6ž†ÞW ,("‹xi‚ ”@~õ8À‚ L$ä�DNÔ@@!ù,#$ÿ'H° ÁƒXȰ 5‰"JœèC‹£‹3^Ôr# Ã…E5šð±d¢-]Rª\™rË£G9D‚dÉ…'Yêì²%‘F‹0¡Ñ¡›rî\Ù3Ì#52=jåR•=„iääP©F«^ŪÀ¡‘„~,ºˆ¢E�pµø�èð�Ú�jmÙË·ï-�¼!ôkÃc»8€4há’E�]ÜXæÝÉ)WNû8¨ä«š+¾aðÆç¥¡΢1Ëé�©‘~]âµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà$²kßν;wæÁ 7{à(q ð*ÏW'o}Jõ›Ù�==½x¤ò¯ÒßR»ÿ–û7å·Ôy‹�ÀHaE`Iî¤ÞTQÅWÞ|_Ñ‘WëM¨ŸajÀÄY†í}ôÓ‡†È�Sj\6ž†ÞÆ �,("‹xi‚ Ôˆ~õ¨È‚BL$ä�DN$D@!ù,#$ÿ#lH° ÁƒmXȰ…‹ 6"Jœ¸�‹3^Ä¡cÃ…-r̈ð±¤�/Rª\™² ¶`á‚dÉ…'Yê|QÃÆ†[´a‹›rî\ÙsŒ 2=jåR•=옱äP©F«^Ū‹‘„~,ƒ¢E�pq¬�è�…Ú�(ÔØË·o 3Ô!ôkCc_ÐØ€4há’-�]ÜXæÝÉ)WNû8¨ä«š+ÖaPÇ祡¾¡ñÆé�©‘~-ãµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà.²kßν;wæÁ 7{Þ@#± ð*ÏW'o}Jõ›Ù�==½x¤ò¯Ò¯Q»�û7å·Ôy1¸�Ca-l`Iî¤ÞTQÅWÞ|_µÐ‘WëM¨ŸaÀÄY†í}ôÓ‡†È�S\6ž†Þ·ƒ3,("‹xi‚ 4~õHÁ‚*L$ä�DN¤B@!ù,#$ÿH° ÁƒYXÈ0D€ ,"JœˆF�Ž‹3^T£ÇÃ…!‚àqð±$ *tRª\™’Jž<†¨€dÉ…'Yê¤C…� C„àO�›rî\ÙÓHž 2=jåR•=ÁƒäP©F«^Ū@�‘„~,Ê…¢E�pÕ ‘è°ŽÚ�ìPÙË·/5xÔ#ôkÃ;céè‚4há’!�]ÜXæÝÉ)WNû8¨ä«š+ÖcPÏ祡†¡Æé�©‘~}çµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà²kßν;wæÁ 7{¢#ñð*ÏW'o}Jõ›Ù�==½x¤ò¯Ò§R»?•û7å·Ôy\Da!`Iî¤ÞTQÅWÞ|_…БWëM¨ŸaÀÄY†í}ôÓ‡†È�S\6ž†Þg„x,("‹xi‚ „‡~õhÇ‚L$ä�DNTA@!ù,#$ÿYDH° ÁƒUXÈÐF‹*"JœÈ#È�‹3^ ²‚aÃXx©‚H‘“(Sž$2dÈH6*´9raI•8‹Q¡‡G˜XL¨PÀÍœ)w£¡�…B‰’4‰¥Rx, uèÔªV¨×®B(ZÔÈ6‰+œ]#‘»xó 2aÁ0Ÿþ¶È�E~IìáÄNçšm\õ1d³‹¶ 9ð ƒ+(#åœ8°�>Dç$]4°j ªq²®éZ%ì͈/×NyÛqîÎ0_ÇV9ûepÛÃSWlC8ÛnA½ºõëØ¯ç-r„…�ltG)ú÷ðãO–¿|¬xéÜ̓w“ìûDº'n_U¼�=fCúÅRåEå”|èmÕÑVÖt`NëƒK˜1HŸG=eÈÞ|ýÈ”d‚ˆ`„+LáHâT!Cd}æ×@Ø¥—^A¬¸ÃD<öèãD;!ù,#$ÿ+œH° ÁƒwXÈP!;"JœX¢‹3^DÁƒ Ã…* ªð±äeRª\™Eƒª0Â…dÉ…'Yê,ƒbÇCU¨Pá�‘›rî\Ù“L!2=jåR•=� r¨T£U¯bUÀW¡‹†¡hQ£[$:4‚ö£xóêÍ{aAB½6t ¶ ¢3Hƒ.©‚°ØÃ‰eÖ\rd�ƒt|ÕräÀ< òà¼ÔsâÀc4Ž!½Ó4ÒÀ¬°Öéú&l–²+#¾|{eîÇ»? �=›emÆÃq_y\qcåº/cNõëØ³kÏN]¥ÍHÏ v*üý¹oð7Å“ïžò»tõbËÇF_þÕò(dëGAÿ£ý¥ß…Á…!™�ÑCÿíäÞTQ]– NîaÖQWï�Ÿ`BÀ4]…ëùÔd‰=ˆ›fN bxÞ<ràB"žw“Y¡ù5Ð!{åˆ}FLäã�@NdD@!ù,#$;«H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç� CŠI²¤É“(Sª\ɲ¥Ë—0cŽ !ù,#$ÿ+4H° ÁƒwXÈP !;"Jœè°ŒÅ‹-¢àA†áBg¸TðHrǘ(SªD9ÆŒ™Ž UA1’äB“+sƳ£Á�*T¨ÀňM8uªäIÆŒ�˜ ‰-yRiJžÈp (C©T­^UÀˆÈ®\£r CEÆ·eP¨�èЈW�BPŒÙË·ï\ä4íGbc”ipha’*‹]Ü8æÝ†’­R®ŒöòáÄ›+æa�Gf¥¡v�ÑÁi�©�®^é4cγUÖž|[tÐ×»5÷Vý›ö뜱mæNõpÙÅu_™rtæ(²kßν;÷ë(~>Ú Lbñ‘i�·Yþ<øâ9h/=ðõ$é[E?Ƶÿ1øy¤ŸRâ…�Â…©Ð@€ ¨S|`}ÄàBæ_WI(_…ê1$ÄKhmh^}†ù"gêfXSBxFÞˆû�G[‰èžMf‘&Ø@\èå—_reÄDDiäDF!ù,#$ÿYDH° ÁƒUXÈÐF‹*"JœÈ#È�‹3^ ²ÂÃ…6zL`ñ±¤ "ERª\™’È�!ڨЂdÉ…'Yê,BDE„mØÀbB…›rî\ÙÓÈ�2=jåR•=™ÀäP©F«^Ū@…‘„~,*„¢E�pƒð�è°‚Ú�1‚ÙË·/‘ \!ôkC c‹üˆ€4há’6�]ÜXæÝÉ)WNû8¨ä«š+^apÅ祡ö¡ÑÇé�©‘~ äµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà-²kßν;wæÁ 7{Žð#1ð*ÏW'o}Jõ›Ù�==½x¤ò¯Ò'R»?‘û7å·ÔyB´ÐCa6D`Iî¤ÞTQÅWÞ|_ÙБWëM¨Ÿa1ÀÄY†í}ôÓ‡†È�S1\6ž†ÞgÄ ,("‹xi‚ 4�^~ùÄ‚;L$ä�DN´C@!ù,#$ÿ H° ÁƒYXÈ0†+,"JœØƒŠˆ‹3^¤�aà |xÉ¢�€“(SžìS¤ÈÈêl9raI•8ôa� ˆG˜ð©SÀÍœ)w(r¥a„…B‰’4‰¥R|z, uèÔªV¨×®\(ZÔÈ–J‰ëœ]x…JŸ»xóö¡ÂaA0ŸþÜV€E~IìáÄNçšm\õ1d³‹¶ 90ƒ(#åœ8ð�wDç$]4°ê=ªq²®éZ%ì͈/×NyÛqîÎ0_ÇV9ûepÛÃSWA8ÛÞ@½ºõëØ¯ç-2…÷ltG)ú÷ðãO–¿|¬xéÜ̓w³ìû}º'n_U<— AúÅRåEå”|èmÕÑVÖt`Nëp…K˜1HŸG=eÈÞ|ýÈ”d‚ˆ`„ðáHâT!Cd}æ×@|Ø¥—^T¬XÁD<öèãD!ù,#$ÿ#lH° ÁƒmXȰ…‹ 6"Jœ¸�‹3^Ä¡cÃ…-r̈ð±¤�/Rª\™² ¶`á‚dÉ…'Yê|QÃÆ†[´a‹›rî\ÙsŒ 2=jåR•=옱äP©F«^Ū‹‘„~,ƒ¢E�pq¬�è�…Ú�(ÔØË·o 3Ô!ôkCc_ÐØ€4há’-�]ÜXæÝÉ)WNû8¨ä«š+ÖaPÇ祡¾¡ñÆé�©‘~-ãµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà.²kßν;wæÁ 7{Þ@#± ð*ÏW'o}Jõ›Ù�==½x¤ò¯Ò¯Q»�û7å·Ôy1¸�Ca-l`Iî¤ÞTQÅWÞ|_µÐ‘WëM¨ŸaÀÄY†í}ôÓ‡†È�S\6ž†Þ·ƒ3,("‹xi‚ 4~õHÁ‚*L$ä�DN¤B@!ù,#$ÿ¯ÌH° ÁƒcXÈð*1"JœD ˜‹3^Ô¢$ Ã…Äиò±dŒ-]Rª\™r‹/^à‚…dÉ…'Yêì²%Æ 1/\p…—›rî\Ù3Œ*2=jåR•=„¡äP©F«^Ū—‘„~,ú…¢E�pµ‘è�‹Ú�T´lÙË·ï-4T"ôkÃc»€™�4há’�]ÜXæÝÉ)WNû8¨ä«š+VbPÉ祡΢1Ëé�©‘~�àµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôàX²kßν;wæÁ 7{ž&qð*ÏW'o}Jõ›Ù�==½x¤ò¯ÒßR»ÿ–û7å·Ôy_`!FaÌ`Iî¤ÞTQÅWÞ|_]БWëM¨ŸaTÀÄY†í}ôÓ‡†È�ST\6ž†Þ†4,("‹xi‚ Dƒ^~ù¥Å‚BL$ä�DN$D@!ù,#$ÿH° Áƒ#XXÈ0€"Jœ¡€„‹3^, Ã…($@ð±d„Rª\™òÁ‚p €dÉ…'Yê4ð  °€ �ƒ›rî\Ù³Â2=jåR•=-THäP©F«^ŪЂƒ‘„~,:�¢E�p @�èÐ�Ú�<ØË·ïƒ  ôkCc H €4há’�]ÜXæÝÉ)WNû8¨ä«š+`À祡> ñÀé�©‘~ÍàµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà ²kßν;wæÁ 7{& !1ð*ÏW'o}Jõ›Ù�==½x¤ò¯ÒP»ÿƒû7å·Ôy(@Aa`Iî¤ÞTQÅWÞ|_БWëM¨ŸaÀÄY†í}ôÓ‡†È�S\6ž†ÞW ,("‹xi‚ ”@~õ8À‚ L$ä�DNÔ@@!ù,#$ÿ!lH° ÁƒWXÈP‡9+"Jœ¨¦MŠ‹3^lÓEÃ…:ÜÀ�ð±äŠ5sRª\™rMš4ê@#�dÉ…'YꜳfÅ7uèš›rî\ÙSE92=jåR•=¨€£äP©F«^Ūš‘„~,ꃢE�pÛ¨‘è�Ú�râ¬ÙË·ïš6pì"ôkÃ7cç¤Ø‚4há’:�]ÜXæÝÉ)WNû8¨ä«š+îb°Ë祡f£‘Íé�©‘~ýæµÎØ7g³¬�™ñfÝ+yOö-Z(mÛ,qC6¾ùJåŽ#7ï½™³ôà²kßν;wæÁ 7{Þ’"ñð*ÏW'o}Jõ›Ù�==½x¤ò¯Ò_S»ÿšû7å·Ôy>HàFa:l`Iî¤ÞTQÅWÞ|_éБWëM¨ŸarÀÄY†í}ôÓ‡†È�Sr\6ž†Þ§Bp,("‹xi‚ G~õÇ‚ ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-green.svg0000664000175000017500000000437615257164523032232 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-red.svg0000664000175000017500000000437615257164523031704 0ustar00zuulzuul ]> ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-gray.gif0000664000175000017500000001320015257164523031634 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§ÖáÚIÓá¾z爕ökº¼�Û„XC¸Äk£€80=hÒBB.ãà›‚Ê› ð­ph0Z£QÐ|ºÈ…o Zsé<�µì"RG³}{Êkiz+ð-¸p)˜‘y=~䯴«Ì1N³½É]däªAM;”䯲y"mùx&ÒNœï úù(Ä‘¹�>¡ûû*Ü�UoK4ÇEc^SÈU ½ÙG ×åÒTkÈð¶`È„¦à„]˜‹„Nþ!C†`Ð@;·ôeØ1‚¸E|Å(P2u©‹ºhàTÇ<(£u u;’ÑR1ޤ_�f„pÌ€2ƒ$ÎZ“ºèø¤AAÆw%4nÈ�1)~9†GšcÌ–f®&.æ�¦ ›m®!¢.Û4x �u²BšDóVŸndYŒ—²hxÊ|„²e.1Âb(ž�Âñæ)¿8Yi�ÞÂè!šnG}º0)HÅì#ª’™’ šº ºªC j1³ÖQë)¦j®sàJˆž®»¥�ä‡K‘ÆÒq))&ú¡h±ÍÖQLv|({ �ÕÎ�.”YÇ¿v;dzþ¤øÑj)q|äî»éÎA,)ŸÚ¡í)¦žï¾ïÒQLJxÀšËíòkpNþ"{‡°ì0>u.”ìïsl.x0ÜpÆ O³°Â´’ GÈ(Ks‡º`ûŽb&œòÌ&Ëá1$Þ²1ÍšbzÃÅèF‘ƒ’¤Põ®xÆX2ß ˆ¨5<²Ñ“—âJ)…iù�“•¼U'Y·+ä"Z¦ìc¦éÈRô¢�³+/w9K@�±¨cY¿Xj¡ŠG#C*Ñ©R\ð ì9e3™™Bm±˜ «JKêÂÒÌã†IËZN¡‡îĤ:»9Ozrók ¡€  H˜àôÏ'íéÍ?„ÒzGÀ‚&Ï{ t Z`çsñO)lÒœµå Á@]*S]na‰6e) L8Ó#ô0†½¤¬‰¢œ½Eød™ÓCÖt©7õÃû(À&쮤U�SƒjÒpŠ'LáBXØ>Â`}‰âë†XŽbÅ1!WÛ"½Þ¯q©%ä"Œõ/ÞðfL 7TÄ>Þ­Š<å#B¾�<¯)¾[`{U¯E¢!‡Ð*Ÿb’OFÆÞJX\¬ùÃ-&HIÁ]$ô³Æq†ói�œÚëØ¹“ƒB‘Ûå™°¨êmÿ¬ÊË& ѹ4t}!�ÑËRn‚+yŠ_þIw—¶÷Äž&`ºg&�šÓ…γ–ÝKnŠ:§Fµ©=Ì>&6ÓFZþ2¡Há¹¾Þf8–{kV“G�ëËĦe½’R×Ö*.u®©6>L|°ü‹²±# ÔhwÚpzžQ¡çQÐ÷·9\µ´Á}Šöîú ¦ŸIA0ª$’ÁNô¾4�i:–[�¥d²£àîo't˜pí-꜄M? "Èq¾õm9X YáWœëÔmðƒã;e6€�ktW{ÈÛæóÇí�q•g¹Ó!ö¤Kþi„±ôò¶(ÊüF~ÍœæHXiññ8ߢ¼É<'Ÿè<žr#0ò䄸£)¬íQþ€Ë¦=XÝñ$üÕ†%€›O‘ ûkÿ†v!KÝå·Öh3»‰ïýð¦'áq0ïÙ›ç­öAÄ[ªÅVr��¦ónã?<)¦šæ¸žÑgÅÎÍ[*/-îë>tßÑ­:) èÖÅ|æ�neÿ¾] Xàè1(ú®“^Õ}p;pY¼­Á[MñPÎ<�Îq»¯Nô4-=·ë}¶›í¸-]i�„~¯î­—;ía¿Òξê£Hkè£/ýé{ÚZ–wùõ•Ž6Mæþ÷£p2–ÝøÖGØËu n÷¡ÀdR”˜õÜ\ð¿ï¼Ó{¿å}“ÏWsÔ}9ÇÆ'pÜ'qÂÇþº•:][¾'3èzìfxÀ~]P£@uøW�ØW€è7€ig�àW�8p܆fXðhxyÈ6sv`}¦Ðy2” ê×s¦°wL—6vu‰ä|7�×zBÀuóƒ4yàÑCy#8‡2âGwÜF…øF„nàaW3_÷nNh‚ÉG†Gh†Uˆ‚üç6X I'8 Ù¦q-§†�§t«… =ö�h˜†v(a7‘ èËÆ{ýg„fö‡€Ø…á×{Ù·}$xlЍ|R£b Å9žÇÀœj²$b¹†;Ñ›M‰sãç×(ËF™‰±-‰ ÏYžÇ ›�–¢ ”ù #Æ’š¤àþ›� ¥X'Ñ0ˆ› ‰$Ѱ‘¡!�dI “‰Œ‰!Ÿ·@ Ab•yÒ " Õ)Þ’ÇqŸ,ºàY ry.Šm*²¢4D Jc;R£gyZÉ¡ã›Ó`¢‰¡�Æp_O¤¦�¢p�£¦°™Ob–±r‘ð «P)€¡ÅpŽ"Á¤6+Pz Пü uê¤_rŽ×0°£Ô-Ÿ' `—üvœÑ Þ$ÀšÆp}Ú#0 ê`¦«âcŠ €¨ƒð 뀧«"§ À¥úé—zú tQ`Z J•Ц÷ jZ(”J&É9¹!ðŽ:´Ð�—à�¡Xš�ªÑÐ!p§Šp˜º 0¨9)$ð.   �’` SðÀ�éп:›€­A/A   `ª èªÞ  p««º>Z�;ø®O°¨±¨ÆJ¯B°›Ê‹ªúzÉzŒ  ž{Ýð‡Åz°…r�NC ” {ñº/`î:±v€ pm﫱�€ 0ëj `€+².û²0³2;³4[³6{³8›³:Û&A;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-gray.svg0000664000175000017500000000524515257164523031700 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-green.svg0000664000175000017500000000524515257164523032036 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-red.svg0000664000175000017500000000524515257164523031510 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.515496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn-gray.svg0000664000175000017500000000460215257164523031170 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn-green.svg0000664000175000017500000000460215257164523031326 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn-red.svg0000664000175000017500000000460215257164523031000 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn.gif0000664000175000017500000001041715257164523030177 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§ÖÁŽ;]ñå?b¿=ð¨ê?îkÛνÿ·Žý±> (€héó\çÌ×?ú¥bR Â^Ý*¾Áéo�üƒ˜ü‚׿2`í}rº°4–SŒ/ƒùÚàÄîÅ? ŠÐÎsþQ@J,d’ÛÜ´µCNPuHœ è½§¡¯ÖúY…> @¥8 %ÂΈ�Sþr˜'Ôéáƒÿ[X#ñÁ!v�‹]üØŽ˜=œUz „ßþþVZ/Vsâ5öFç@ÄŒŒMp<|é¡€3„…ò6!D<Ø �)ܘiVH"ª±±�b\¤+I>pteJŽ÷è­ä%òQøè¤ä>É;=„â ùT¶Uzh�z($I>L.‘tÍÊâÑÀ8Æ?9ëzrr%+Ó¸•Q’¼ô‹"Ï´ÂARK–¾¤$½¥.«ÄÒ}Ô¤6+ “Kžï˜7 Ì-ȉ†UvŠ/YŒ#&ÙAA3œ\Hâ_9¨3`Íœç¤ 3%cB|Þq–ü„ xšM3Ð�r)(;þ{™M#½%žòt(�.à ƒ&tŸËܨðLŠ‚ô   w.X>:bð 68CZ†”")�àœ¨KºS&꣭©H»WDöt›GUi£ù›Ú wpœª¬™]£›u�¬¢jT˜¾4¤¡ÔF:Õ7T&UXNÛã’¸ÊT78™»¼ªTµ9?´*«@èˆäÚU9D2©Jå\[QZÖ.Õ®^ý*X 뺿’U¯õ»+³ÒJÊ ‰U`d¬8/‹ØÄ*Ö²�¬F!(É^Ó¤<ý(isÙY·jµAßôÚš(ÓÖ“ oõ(£©Ê>.ö m¬h…ºÚq†v¬žMmr!þ*\Ç6õµ€E*PªZþ²¸Ò£.a± Û Xu—|»îl…À[gZq´Z¥llÇi]äþºì=/qÓÛ]šÆ÷¤â¯E§«ØíÒ7ºøfvûûÞ0á»>ô-zû™¬Þ¶×½®ƒóÛʺJx²îï…÷»\ünø�Vnu?|Óúj—Â$†«‰µ›ââö5Â-niˆ c…² z®ñçª^ÛRÆ9öñ�y¼b!¸8±‘E«[/Ù“(†ò“�ìW�ÎxÊ"v2“±|Ú+?˜Ëxõ²~Á,å™Ër´n”É,Ý —™Íáõ)UuœfÃy‘Àœs‹Ý O+‹¹ÆuŽó�ñ»þæÖjyʲ=�2•Œè@yÐmvs—ÿ|áDc˜Òµ²ô¥1½ª�XÒÖ´¡cGC˜Ô$õ‹QýaOCIÏ�R5‘Ï*YS™Ñ¡6õ¦A})[û™×±Öµ �ëN {Õ¬–ÔD¶¥ûöÉ×1H2§Ÿm3÷xNžp±Ÿ}lkÓšÚÝ–±·M¦jã@ÚÀ¶Q¶?=m ­ûã.´–ÞÍît‡ˆÞ˜ ·y}s߯†õ}p�‡'Áµ!¸uþ…'}ÃùÞ+L|=ÿÂÅÿ½q*tüáφÃÃ_Øø{-%·ÂÇE“r+Ä;Ù­ÁCÄÓbn³à�Ù«È8ËWΘêš3Eç4÷ùLZþ  Ã…è}áùQŒN ¤§ÄéÔP:Ifê ¨/„êE±º@´ô‘ÏëÖûÕÅv²ï„ë²xy»™á`~´(æÝzÜ·ñv„4×wïIÞÁ±w¡ ÛÂŽìZþ~r“Ï}èh˺à ÃÙœ'~ç�ïúá¯Bx¯ã縲¹¢ËñÆkœó×¼nDo Ê›=¦gu7K މ™hšÚ}ŒøÕ䜖Ûnn—²íÆ{yƒ»÷)Û°é�ïù\?®7þñûNçå/Ò„>Zò¡o_‰RÖ�˜þõ·Ïýî{ÿûà¿øÇO~0G;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-gray.gif0000664000175000017500000002041515257164523031277 0ustar00zuulzuulGIF89a,,òüüü€€€Ûèààêäììì!ù2!ÿ NETSCAPE2.0,,,þºÜþ0ÊI«½8ëÍ»ÿ`(Ždižhª®lë¾p,Ïtmßx®ï|ïÿÀ pH,�ȤrÉl:��¨tJ�>¯ØŸ Êíz¹‚¬x|úšÏèh˜ÌnOÒð8z஋åø|šnïõ€�hk~…9‚ˆ‰g†Œ/[Š�‘]�”%’—˜\•›™žŸQœ¢o š�¥U£ª¥1­«�™<ž°~—’µm‘M¼ºX�wŠ¿½‰uÃÄGȶˆÉCÆ›ÍÎ>‚°ÕÓ:�Éר5€ÝÚÝ2ßâ áå+äè êë–yîzñ#óôö÷§rú¤xýàôg`�)à!”°Á¿†ðð�h® E f¼ˆþÑ¢A~;xì2¤È‘ñPšÜ—æžÊ•à¤l ót/k‚ÈYK¦Î>·Ñü t¨® D‹ÞÔ5iÒ2FWE}Š‚g¬¥TYX-45« §Wy…–«Ø±®ºÚÁŠ6íY³fÚzc»ö­ÜqtwÙ½‹w|ûÆÕë7°ÛÁì…�âÅŒ' C ù†Z%�+G6õäÑͳ #É zsªb”KJ=Z´ê¹Ÿ1»~ »qÒ´gœ™ˆ‹uîÕ¾µàþ­;8µÙÄkK2=?þû_èŸyîiwÚ€ð hB{ý!HÞë‘U ƒÿM(aƒ§ øeÞv/lèá‡UðÖ‚tŽÈœ…²¨"w.Vã‹¡a(ãŒ4fƒc=;昞ˆõèc}6Ú$Þ� U‘HÖ¨$ >Ù¤“ Yå”02�X†(d�ZvÉß•[~)&�\˜È¡™gꊕ)¶©¡”1±)'‰ûñæ�×Ù)ŠTð™àž*(šq~Àå¡é‘ŸŒÊç¨B“Fêe¥ø@ji£tf°è¦7vz�¦ *%꣧–êæN¤ªº$™…¦Š*¶ÊŠ­®’ ^�²æêB‡ à꫞½:ðé°ºjþz,²kb¬°Ì*ú…š¨Â-žR°èµc&Zí¶ÜYì³ã†ûªµ™–k.œàÆêíº¢+�³ðû.AíÖëf`Þ«¯¤åÒû/«˜ <0¯ãšíÁؾ9ªÁ o  ´›/¥êV|Ò¸kŒïÅçé1§ò:ñÈOAmºþ¢l/Jp²Ëƒá®È4ŸÛ²É%ç¼q¯Q‚ìs³ÅÎ<ô*ìðÑÉNj4Óì|4ÎP\tÆUÏëèÔ g]¦ÓX{ÍóÕ=‹²1³¼³Ùg«q3Õl¿ÝuÊsÇíi¥3ÍfÞGïýtÕ~‡möÄëmgá}.¸Ø">tãŽç a¿kþÛ=vå— m¹ÖêòM3�‘» úâ€C:Ê£“n8½Ë>®¬çsٺϋ®qí¶3 lE§ßIëËšÓ]÷æ™Ã�×ÃOnÙ2÷>ð²³� }îÿ }ìOS_ïì×G̽öëR¬ôÒÄ_ßýóŸ_}úà_Ûýøü*¾úðÎß>³“›@¿¹ôïÏmÿ÷Ë•ÿü‡¿ÜY< µ§@WpwësÞò˜W±B°~ù»O-µAÞ©.Z”Xù /yÒëà1§¯j‹‚áSaÔ$HBÎ�†r2ÃÊâC÷Ù°xÁkáÕÖÃa¹ðk8Ä’©TDþ�s<Ô›–DMþxAáÛ†ÂÿmQ'ìb‡(·,:qZDh¨®ˆø‰QUl4U¤Æn5QLqØÑ“G„ñ�‚êc þH> Rƒ_|M�@H8òW�Ï"W”ÈÊ "1•\Ì$ÓIâl’“�Tä'{“ɱ\& §|QÒ†ÉR&¥,„q¥N`é†T"ˆ–ÇeràÀʺ„Ò”¸ìƒ-%Lfüò•Åô! ù^e˜¹�Ã9‘LQêҗǼH5©¹MÃlÅݼË7{ζ”sã”K:±Î¬”„ï L<áÙÎYžS(õ É<²O¢l"ÿ´g>ݱL3~$ úD(@j„,´Ÿ¡5É?êNŠÎ„¡Oqã�йh”ŒÊè¨<ÏÁN’BFà©fPÊM“ªæ£Ù—Ji lº”:˘ Kéã Ô@ÃA0(¢~ê!Zdé½¼å+VS$¡ÂxÒzjüð(U UU–꼪V!z§­z¤Ô êWo®±¢"©c4kSo§Ö€V¤Ã°8{Q‡°–n €R�h°ËºÆÆÈïðñwxxæèé’· €‚uvx`–s…†‰v¥ˆŸÀªöøöîîð}~€H†^¦¨©«ÈµÐàÕÇÈɾÀÀiijopq¥¦¨�ž ÏàÕ¿ÕÈææèÛèßÝÞàÎÎд¶¸•–˜®°²ÕÖØž ¢ØäܮɸH‡`›½¨ÖØØP‹emnp?�Xßàà·¸¹¾¾À���¶ÐÀ¶¸·ŽŽ�‡ˆŠøøö�¶žÏÐѯ°¯ˆˆ‡¨Å³—˜šØØ×îðïñññ–˜—·Ð¿� Ÿ­®° Ÿ ÏÐÏ€ÆÈÇ���Ž��þ€‚ƒ„…†‡ˆ‰Š‹Œ�Ž��‘’“”•–—˜™š›œ�žŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆ¸ )&Ê)&JÍÌ& ÇØÙ·Ö+-çèéé*$8 ;#I ÚùúŸLâæÔ H°`"t¨€oŸÃ‡‹*\ˆð ŋÕeXP¡Ä�ùR\p�±¤É“(8zɲW’NÊœyÒÁІ-sÆ‚A’¦ÏŸ%]¬8¡³è© H]Ê£�)ŒJí´Å�¦X³Zô`bªWJ=µŠ;°Æ×³‹�Ug ƒ‡þW*]€@1æÚs8* Ý `G² ¤‚€�ì³|�êЊ¡­ÄzHܲDSvôò ”©Á”:øÓÁ…b-:ÿD° ´6viˆ0"ß‚Ñ?3tp]l„eš*J´†x›´Lµyÿ¢ë“Df�J/©" å»:\�‰ Þ× Ó3²àŠýVaîÃ÷ºP1Óâò±ÔÎü¡‚ì®ð]m?!š:ä·Êì™d@zîp '! —€¥¤VFì¡!ò•ôCA\Ê(˜Äš‡‰DpÒg$r’„ fdÀd)*™I<c%oLx72›�+ô8Éá´hB.þ‚[I&É:dN>r^FàT¥!0I’[6B¤†+…9ˆ}.š) šáf±]Ô¤š‘¨&g˜;”D%�–””A•&f4'Ÿ”Ä)%BÚi „jYFUÜXŒ�n’‚¦Y©'3Zô'„†äÀ¦ \€Ñ 6bê@�:Ê¥å­*®’â¥:§ñö›EýÕJJˆ•á ½çk(»Ô+ekJ •p _¤‚³§äyQÓžu+:£b‹ F(¸9Õ·ç`*n)ä*áUEÖ®ËÊ~�jNWDŸ¼¬äKОÐY0¿«D)ÐÀ eø/Á¯¬�,­P-« þÃâÂEË>”�¬èTK§ùPåêqéÕ>&\æÉ® ®>ð„̳L<�ÉÙxpγ\qQ·Ç(ñ3г(<е؜p4Ò³@k� ØÔü%Ôµ´`ƒA"Ã&AcM‹Ï³ÂÓ  ö$�”Å00 ½6-8d¬/$„ðÜ�¨Ê×e L—ñŽR@$®øâŒ7îøã�G.ùä”Wn¹äˆà7)þª³Â½¹lÐ6A”‚"À ¤Î:EÜåúë¥^Pê Pp€)‹¶Ú‹Ò��Ò@°/úQsP@½8�h@�v$€ßÆhŠt¨áùXÁÈAx1ZþP,ˆ¬(#A2@Vü‡ L£ÅP€(D{‘@A4  ‹€¡·úÔ+Œ¸Èô`³ �Tà7ÈQ´Ì…Ôâ¢�¢eIž,B…‚a�c€H8sR€cªã�Âà(´ØÕËz60ÊHå)Œ�¢ƒ Ph1€¸„F!M’€›à aÅ„²„¡Lʵ¯ú€A(\“än™HBÙ„ÑÙÙ½ÕXB¸�¸%(λØ®ã6 8ò&μ@¯zËËÞóBîÍÀWc'äâÂzûâ„l×J\ƒp ¬d8ønàƒ'À>pn�€þ;øÁް„'LáÌ ž�|;Ü  "ðˆ}oag · yÝÅTÂ€ë ¢„c2 Q[ƒX`ĸ@'îZÚaÀ¸m!t FtLA„X%E;‰ã[,fa›Ðb‰!ƒÓ¢#µƒðZWäP@mî´$�²YbLWRˆ1ài{³Ëà+�=J$p8´=ÎÇRÌ‹ô³ _.D …"[€Ã+&s6nuÐIœ¹C°È�­LC!€ ‚p,‹ï, 4ö.`x41”[QCøô‹Eà!�Ì¿Ÿ6Y·ÒDdÏÛc¼¹ ‡6DÆðEŒÑ »È^� «þ©ƒ ™päl�ñ÷íÕ!ˆA¡…˜ëBà 6æô0¬×µ>åy~{ÀŸ "àR,³ H]�!à!=†—ÎI‰ ˆzÍQ7ðáNaŒ2I÷Á1LV 7ÓŒ�mìzÁ4´> Q‹ÌU›²%\¸3m„ D°– ±„ÌbÐ݆ ÁÖ ’€²æÃÓê`ˆ·5Ž µ… ƹó¸ŒAÆšÚ"3ÖÇ”�Ž 4º÷Ɔm;ñA<@Óþ+7v�‹¸X²*A‡(!„¤Cñ4H™•œÎ ð!]2 ãkŒŒ „:uWV‡È`éA‚ÚŽH“�þO%» ¢mlÀ<|VÄ~Ï�*|Ì‘µy€°Ã�H6ªi�‘?D”É€áÚh5xˆ’¼Ê >|˜?Du bƒ«ëÃf&vÄ·÷1{cA›ÄØÅÇÜC €Îi±ƒalÉFÊÑ47F%-Ÿó Ìï„<ì|�ˆSäËF=Ï!Í'í~½HZ?€x›Á•‹Bp €mƒÜÀ 9™uꈸ ¤î €nÁʇ\P°‰PHpO�y,zê@oŽ@�ö”X€XP!Ug; T^p_°~ƒ5  ‹q‡~þg"€Bñ2 Pk�ˆÐq‚pƒ1I’à0E1ãÆr¶—‚€sL1]à[ñuQFá%°C� +¹eya.g\À+Ç_pC46w‡0UÀ²b)õçÞguhçƒÐhóSاkȧN4 ‚Ä0~,Ðmº}˜‚@x @DH< Q0t` °aÆ7ð€áMà„å—<¦f9J¬Diê@Ô³‡–È npEÀðÕW®çw…¤15GuŽ·]G1-±9jCP¸X6ø<@„@ @°�þ`@ÛH Àvƒ�zá�gÇ ö6M~óW‘1@=ÎØ~‘�ŽÖVˆßGŽ a„��Ì”„8tðw>—‚ÀW‰Ž`Dd xÚGPŒg!�Ž0ÁòX��‘pétc‹ˆP y[(¸õ ǃè‡i  †`‹Œ°9é(€²8GÙL ³vzRq:'q@ ‚ `g0”DI,0zŠ�Š@°„%ãX�2ÀHcT gX7@ˆ¸^b\™O¶l{Q:( @�%) Â˜þ1`‘‰á%,PJŽ€„gqD=ý&Uµ“‹P\4�$ÉŠ©T�à˜¡f 4!@ƒHcшð’¼Á1Bð0~®¡M~6‚¥TlaúF“{‰ VçàÈŒÒÓWy˜€O!—Qz`.‹p¥X Çy™ y@ð’(²¢2�pV_i�Š Mǧ›Á}ŠàMUg9à‹Ä�h�Ò©±éˆÉNÀy'Qƒ=ÛI{çp•ªR¯ù²®†&!F�pk21…÷Ã…‹° É`Ž’¨Xy©9m)Mø� wÉ_y3þ!]𔑠–AInN€•-¡˜À‰^™"úŸ 2J 4 @-Ù–èð8 sY—ˆ0k«¨ÐxJˆøRP˜ÖÔ¤E¨•ŒÙG‰}W"yÙ @0ƒè ¤é0l|“�€�ç�xQ¥ Y朂™eþ¸W~j túB3™ó%‰÷ˆ¥‚—(ùm@q§ŸEŸEQ”�àÁB:Ñ{¡nƒš‚‡ð ž,†r_Q–µ©Ѫªå¦ê qè„"`©ê�§¸ª ËøvSQ‚ªÙt¨p«@š‘µ:2, =¤Ÿ !¨š70‚þ«p«–$«Q|R±Ž4‰wX“Ð:9€­­ ­¬ä¡éhFá•0( ãªí—ƒ’øVß™ƒÊ®ƒ!ªRAžp‘@ž:¡„Ë:†z¬ºæ®ç`€F1‡GÇ£1¥ù a{£ÊþŠ¥ÜZ}^Z L¥‰ppé�Œ¼W£r ûb\Š-›•Qk‘P€¡°"×±•ð²‚÷ž,ǰŸ÷B“¬x© °H}ê°�.«Éy[# ú—¨ŒP¯Å e¤Øf³´X¨Ÿ5µ¿`=›X{ ½é ^+=°µA´ú`=áj¶Òš pž÷´ç) ´àÔþ°!+pK“à0u n ˆÙµ�ðz1=¤ú6ƒ¡�ð‚ðàÔšo¶Ð¶ÖæŸÝ*¶»°Žøï¸>ñh K«¸à¹Ö’çp¢Æ°¢ê ²�­wK €�³Ó¬ªàºIf…éAž •àuŶèÀž·¼/»c ˜j ²b–À1÷ƒ P•‹å»¿ Çg’«‡‡A¦’°Žº„ NÀ¹º½/”½šòª¾’€³1YÒG€Pu >Kò+xð›À⊖ X| ˜9K˜Àp 2³P »Á +6;s \ :åÛº“Ðlñù ‰ËÂy§ÜþkªJI�0À‚9¢pqØ`=ëë«Åµê¾ŸÁ0LÌ{¢K ±– ) ¼¾ð=Ë%�\I‹ô§MÄ Õr‹g¹ JÌ ƒ »L lº’Àà�`rð� 9‰@Å/æ®@Pı ¯¶+ äÉŽY ·q›�Ù7j5Š ` ŠÄHrLçš-L c°™`§£»° Zúh ƒ‰¤?ÁŠƒ Ç‚³{; ] ­¸ÁÀ�·qY jyªX1  H£L LŒk| ´›©K xn<ܼNL I>ÀW¤œ}ÀE¨ÑÔ ëXžþÁP!{3ÆŠ0˜ÞKGpФlÍ¥ff�¿—p¨[Œ �›"€ˆ»¬AÚœÅ|Î (Ë¢š°ÊIÛ C|c•СÃãR` L‹ñ\ )¼ ·bµ¸ÐjU ý<=d˵2Ÿ�Dg[ ˜e– C|9€Ñü*Ã!ý aΘ�ÄM \}-|ã3#[c, 㩠ͪ­\­pÌy̓…’�̉ʱ½ÒœÊ´ ¼±; ÐW ;PàÃÐDíÏÀ üë ÑŽ}FÔ 0²0æ?3 PÝrÕKb‘Ç™Àq¬Œ 7ù¿’PÐÏT“°ÎWÖþË ÀT;ª +E…¥ |}z- óü< `¬ŽàÊßû ãÐ=ýÒ¯Õ^¶|‹=<‰ ��I*½¿7œ ÷œ'ì C,�ëÖœª“:EÀZÛ¯mFðÚ©c۸ͶmÛE@Û¼­ p70Û ¬�œ’Q¿»�Ð0T ·òÕ¶à¡K K  ÐØ�ÝÜÝ1°ÝÝ ÞÝÝ]ÞãíÝÙè10¤’pÖ1ÓV´¿[W -@·»@ÔÿÄɤºw!ÐßýÅ–¹w‚Мï&àJjó!€ví·Éº<Ä&í×�p¾ŒÇÒŸ  šª #VvqvL 0-™Å¼ þ1ß§p+…} pÌ#*§_A��ëƒ-î ëè–K –:| %Þžz)ÍÀ7UÆZG´¯¬°=Œh 1­b� Pã²7Î_°A¶ �9>@>í‰Ð-‰ÕÕAŠ­°=L�OQº>þF çmäþ;Gtçxžçz~ç( Gr~¸ü zؤÀ1Ál8# g%”p‰]§ðÑì . e©EŠ~éUßûk²¦°=ª@d—~éŒ ßÒä«j…Z ꡾¶¢àC°ð-e» ¿Ãê¡Nd~ V c²À©Ì¸?DmëRÔr© %œž ÛsÚ ,ÃÂ.DÄ›âþ¼> ¾>×ÞSÈÍ^B®Þ ½¿·�û·?‰~í$ä´KµÔ·pºéœ z*î_”íœ ¯Ãy_zS ‡Íî4êåN(^ àíp­ µnï5$%á‚ 0»ߢ³¡0˜€I²#ðãî�S £*> Ó ¯ 'ɦnÔFotò%�ò*¿ç,ßò{þFk 1¿ò+oònt8Ÿó€ž^1õqõSHY 'ïÜ£ §I(JÖõb¿`͇ϟ�ô|³ÛS„^ úëÔ�ñârÀe1 sgÙSïèœðËÀL  çÀÓcŸ ~ÉÄ@Û+Þö¨ðñû.þ Фõvß cR´bkrÓ÷¥ðñÏm èžî„¯ /Ýİíb¿ø”PÚê`áðÇp/ù”e£½‚®ùœpÊþ ¸ûå Ÿ i�ÿðñ }úCrŽŸ `öæû•ðñçpìØr=nŽûŠ ûÐùØ`�^”-ü„ öè°æ ‘øˆÊü>¢31úÙ�úSCý‰àüéÂû¡ûíËý‚@üpõávÉýÄ蘟ùÌOüðþ ±ÖOíšoûþ9Q,€ 8HXhxˆ˜¨¸ÈØèø éPiyiQ ÉÙéù  ‚9ZézŠšªºêh@: Á*;þëøóŠ©B«»Ë;{‹YÓ+¬kõ{1œ¬¼<Øa\‰PÁ< ñ\i@­½}Jù\bÂ-þˆp;ž®~Xllp±ˆaŽ#¿ qm0‚ï�Á\€yy@±OZÁ{ÖÌ[Õ s„D¼·C ‰‹% ÌÐñ^’ræà…<9O I”ñ<DÀ’¥>�)bÞ«àÊœ›;ˆ2“ç=  ýw!§¹GýU@(ðGÓxJÍ)œŠï‚ÀRX¹m­²ë?*ô¶º»¬êµ-AЄñ5€)·»Ô>³±€î ¿†Õ«ªïÖWÿ;1"n€ {Âa`ËWáC¬PB1t–þEh÷uAÍÎ…,°{ C^Òˆ" ~æ -ë‹0¼ÅEÀyvѯ$€˜Ýñ¶ãî|¡l\*)¤‚`ã«Ñv˜þU…‹ÂÏQ®P|ɯߣ; ‚€‚{÷˜‰Ï[Bð¥ ÷–0$Y�õÄiú•¬Dh ‘YðWIzø¡Â$€dP�:¸@ÂkŠePÅ Ò%Ķh %D°@?ÌŒà� �Ä ŒðÛ†€�°‚1h `à@-¬´J @à@ ü„£.\aÂh2ZVÁH†#)ìØ# \°%�‚\q—CBA+ZÈ .Œ á“Ï]Y•r§Îù Ààæ†”‰&�~nÅ Ę瓈áðg¢·-p_¡Ž’vŠNj‰#lðh¦†$±¥:°¡šŽzˆ Hêi\(,P ¤¾úH²˜ê-%,C›°îÚ‰ #I럔àÁñŠì,&À0Â8€�°ÀÂ/Ò¾€A\°B&èšl¸âŽKn¹æž‹nºê®Ën»î¾ o¼òÎKo½Ü;././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-gray.svg0000664000175000017500000000554415257164523031337 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-green.svg0000664000175000017500000000554415257164523031475 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-red.svg0000664000175000017500000000554415257164523031147 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2662628 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/0000775000175000017500000000000015257164606027420 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.3482609 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/0000775000175000017500000000000015257164606031060 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000024700000000000010220 xustar00140 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/actions-checked-selected.template.html 27 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/actions-0000664000175000017500000000026715257164523032523 0ustar00zuulzuul $text$ ././@PaxHeader0000000000000000000000000000022100000000000010210 xustar00118 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/check.service.js 27 mtime=1790765394.516496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/check.se0000664000175000017500000001022515257164523032464 0ustar00zuulzuul (function() { 'use strict'; angular .module('horizon.dashboard.project.heat_dashboard.stacks') .factory('horizon.dashboard.project.heat_dashboard.stacks.actions.check-stack.service', checkStackService); checkStackService.$inject = [ '$q', 'horizon.dashboard.project.heat_dashboard.service-api.heat', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.actions.action-result.service', 'horizon.framework.util.i18n.gettext', 'horizon.framework.util.q.extensions', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.framework.widgets.toast.service', 'horizon.dashboard.project.heat_dashboard.stacks.resourceType' ]; /* * @ngdoc factory * @name horizon.dashboard.project.heat_dashboard.stacks.actions.check-stack.service * * @Description * Brings up the check stacks confirmation modal dialog. * On submit, check given stacks. * On cancel, do nothing. */ function checkStackService( $q, heat, policy, actionResultService, gettext, $qExtensions, deleteModal, toast, stacksResourceType ) { var notAllowedMessage = gettext("You are not allowed to check stacks: %s"); var service = { allowed: allowed, perform: perform }; return service; ////////////// function perform(items, newScope) { var scope = newScope; var context = { }; var stacks = angular.isArray(items) ? items : [items]; context.labels = labelize(stacks.length); context.deleteEntity = checkStack; return $qExtensions.allSettled(stacks.map(checkPermission)).then(afterCheck); function checkPermission(stack) { return {promise: allowed(stack), context: stack}; } function afterCheck(result) { var outcome = $q.reject(); // Reject the promise by default if (result.fail.length > 0) { toast.add('error', getMessage(notAllowedMessage, result.fail)); outcome = $q.reject(result.fail); } if (result.pass.length > 0) { outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); } return outcome; } } function allowed(stack) { // only row actions pass in stack // otherwise, assume it is a batch action if (stack) { return $q.all([ policy.ifAllowed({ rules: [['stack', 'check_stack']] }), notDeleted(stack) ]); } else { return policy.ifAllowed({ rules: [['stack', 'check_stack']] }); } } function createResult(deleteModalResult) { // To make the result of this action generically useful, reformat the return // from the deleteModal into a standard form var actionResult = actionResultService.getActionResult(); deleteModalResult.pass.forEach(function markDeleted(item) { actionResult.deleted(stacksResourceType, getEntity(item).stack_name); }); deleteModalResult.fail.forEach(function markFailed(item) { actionResult.failed(stacksResourceType, getEntity(item).stack_name); }); return actionResult.result; } function labelize(count) { return { title: ngettext( 'Confirm Check Stack', 'Confirm Check Stacks', count), message: ngettext( 'You have selected "%s".', 'You have selected "%s".', count), submit: ngettext( 'Check Stack', 'Check Stacks', count), success: ngettext( 'Checked Stack: %s.', 'Checked Stacks: %s.', count), error: ngettext( 'Unable to check Stack: %s.', 'Unable to check Stacks: %s.', count) }; } function notDeleted(stack) { return $qExtensions.booleanAsPromise(stack.stack_status !== 'deleted'); } function checkStack(stack) { return heat.checkStack(stack, true); } function getMessage(message, entities) { return interpolate(message, [entities.map(getName).join(", ")]); } function getName(result) { return getEntity(result).name; } function getEntity(result) { return result.context; } } })(); ././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00119 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/create.service.js 27 mtime=1790765394.517496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/create.s0000664000175000017500000000564115257164523032513 0ustar00zuulzuul/** * (c) Copyright 2016 Hewlett-Packard Development Company, L.P. * * 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. */ (function() { 'use strict'; angular .module('horizon.dashboard.project.heat_dashboard.stacks') .factory('horizon.dashboard.project.heat_dashboard.stacks.actions.create-stack.service', createStackService); createStackService.$inject = [ '$q', 'horizon.dashboard.project.heat_dashboard.service-api.heat', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.actions.action-result.service', 'horizon.framework.widgets.modal.wizard-modal.service', 'horizon.dashboard.project.heat_dashboard.actions.createWorkflow', 'horizon.framework.widgets.toast.service', 'horizon.dashboard.project.heat_dashboard.stacks.resourceType' ]; /** * @ngDoc factory * @name horizon.dashboard.project.heat_dashboard.stacks.actions.create-stack.service * @Description A service to open the user wizard. */ function createStackService( $q, heat, policy, actionResultService, wizardModalService, createWorkflow, toast, resourceType, ) { var message = { success: gettext('Stack %s was successfully created.') }; var scope; var service = { perform: perform, allowed: allowed }; return service; ////////////// function allowed() { return policy.ifAllowed({ rules: [['stack', 'add_stack']] }); } function perform(selected, $scope) { scope = $scope; return wizardModalService.modal({ workflow: createWorkflow, submit: submit }).result; } function submit(stepModels) { var finalModel = angular.extend( {}, stepModels.selectTemplateForm, stepModels.stackForm); if (finalModel.source_type === 'url') { delete finalModel.data; } else { delete finalModel.template_url; } function onProgress(progress) { scope.$broadcast(events.STACK_CREATE_PROGRESS, progress); } return glance.createStack(finalModel, onProgress).then(onCreateStack); } function onCreateStack(response) { var newImage = response.data; toast.add('success', interpolate(message.success, [newStack.name])); return actionResultService.getActionResult() .created(resourceType, newStack.id) .result; } } // end of createService })(); // end of IIFE ././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00119 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/delete.service.js 27 mtime=1790765394.517496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/delete.s0000664000175000017500000001035515257164523032510 0ustar00zuulzuul (function() { 'use strict'; angular .module('horizon.dashboard.project.heat_dashboard.stacks') .factory('horizon.dashboard.project.heat_dashboard.stacks.actions.delete-stack.service', deleteStackService); deleteStackService.$inject = [ '$q', 'horizon.dashboard.project.heat_dashboard.service-api.heat', 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.actions.action-result.service', 'horizon.framework.util.i18n.gettext', 'horizon.framework.util.q.extensions', 'horizon.framework.widgets.modal.deleteModalService', 'horizon.framework.widgets.toast.service', 'horizon.dashboard.project.heat_dashboard.stacks.resourceType' ]; /* * @ngdoc factory * @name horizon.dashboard.project.heat_dashboard.stacks.actions.delete-stack.service * * @Description * Brings up the delete stacks confirmation modal dialog. * On submit, delete given stacks. * On cancel, do nothing. */ function deleteStackService( $q, heat, policy, actionResultService, gettext, $qExtensions, deleteModal, toast, stacksResourceType ) { var notAllowedMessage = gettext("You are not allowed to delete stacks: %s"); var service = { allowed: allowed, perform: perform }; return service; ////////////// function perform(items, newScope) { var scope = newScope; var context = { }; var stacks = angular.isArray(items) ? items : [items]; context.labels = labelize(stacks.length); context.deleteEntity = deleteStack; return $qExtensions.allSettled(stacks.map(checkPermission)).then(afterCheck); function checkPermission(stack) { return {promise: allowed(stack), context: stack}; } function afterCheck(result) { var outcome = $q.reject(); // Reject the promise by default if (result.fail.length > 0) { toast.add('error', getMessage(notAllowedMessage, result.fail)); outcome = $q.reject(result.fail); } if (result.pass.length > 0) { outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult); } return outcome; } } function allowed(stack) { // only row actions pass in stack // otherwise, assume it is a batch action if (stack) { return $q.all([ policy.ifAllowed({ rules: [['stack', 'delete_stack']] }), notDeleted(stack) ]); } else { return policy.ifAllowed({ rules: [['stack', 'delete_stack']] }); } } function createResult(deleteModalResult) { // To make the result of this action generically useful, reformat the return // from the deleteModal into a standard form var actionResult = actionResultService.getActionResult(); deleteModalResult.pass.forEach(function markDeleted(item) { actionResult.deleted(stacksResourceType, getEntity(item).stack_name); }); deleteModalResult.fail.forEach(function markFailed(item) { actionResult.failed(stacksResourceType, getEntity(item).stack_name); }); return actionResult.result; } function labelize(count) { return { title: ngettext( 'Confirm Delete Stack', 'Confirm Delete Stacks', count), message: ngettext( 'You have selected "%s". Deleted stack is not recoverable.', 'You have selected "%s". Deleted stacks are not recoverable.', count), submit: ngettext( 'Delete Stack', 'Delete Stacks', count), success: ngettext( 'Deleted Stack: %s.', 'Deleted Stacks: %s.', count), error: ngettext( 'Unable to delete Stack: %s.', 'Unable to delete Stacks: %s.', count) }; } function notDeleted(stack) { return $qExtensions.booleanAsPromise(stack.stack_status !== 'deleted'); } function deleteStack(stack) { return heat.deleteStack(stack, true); } function getMessage(message, entities) { return interpolate(message, [entities.map(getName).join(", ")]); } function getName(result) { return getEntity(result).name; } function getEntity(result) { return result.context; } } })(); ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.2742627 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/0000775000175000017500000000000015257164606032011 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1790765446.350261 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/css/0000775000175000017500000000000015257164606032601 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000023600000000000010216 xustar00131 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/css/angular-material.scss 27 mtime=1790765394.519496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/css/0000664000175000017500000203720615257164523032613 0ustar00zuulzuul/*! * AngularJS Material Design * https://github.com/angular/material * @license MIT * v1.1.5 */ html, body { height: 100%; position: relative; } body { margin: 0; padding: 0; } [tabindex='-1']:focus { outline: none; } .inset { padding: 10px; } a.md-no-style, button.md-no-style { font-weight: normal; background-color: inherit; text-align: left; border: none; padding: 0; margin: 0; } select, button, textarea, input { vertical-align: baseline; } input[type="reset"], input[type="submit"], html input[type="button"], button { cursor: pointer; -webkit-appearance: button; } input[type="reset"][disabled], input[type="submit"][disabled], html input[type="button"][disabled], button[disabled] { cursor: default; } textarea { vertical-align: top; overflow: auto; } input[type="search"] { -webkit-appearance: textfield; box-sizing: content-box; -webkit-box-sizing: content-box; } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; } input:-webkit-autofill { text-shadow: none; } .md-visually-hidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; text-transform: none; width: 1px; } .md-shadow { position: absolute; top: 0; left: 0; bottom: 0; right: 0; border-radius: inherit; pointer-events: none; } .md-shadow-bottom-z-1 { box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); } .md-shadow-bottom-z-2 { box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4); } .md-shadow-animated.md-shadow { -webkit-transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); } /* * A container inside of a rippling element (eg a button), * which contains all of the individual ripples */ .md-ripple-container { pointer-events: none; position: absolute; overflow: hidden; left: 0; top: 0; width: 100%; height: 100%; -webkit-transition: all 0.55s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.55s cubic-bezier(0.25, 0.8, 0.25, 1); } .md-ripple { position: absolute; -webkit-transform: translate(-50%, -50%) scale(0); transform: translate(-50%, -50%) scale(0); -webkit-transform-origin: 50% 50%; transform-origin: 50% 50%; opacity: 0; border-radius: 50%; } .md-ripple.md-ripple-placed { -webkit-transition: margin 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), border 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), width 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), height 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), -webkit-transform 0.9s cubic-bezier(0.25, 0.8, 0.25, 1); transition: margin 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), border 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), width 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), height 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), -webkit-transform 0.9s cubic-bezier(0.25, 0.8, 0.25, 1); transition: margin 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), border 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), width 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), height 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), transform 0.9s cubic-bezier(0.25, 0.8, 0.25, 1); transition: margin 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), border 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), width 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), height 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), transform 0.9s cubic-bezier(0.25, 0.8, 0.25, 1), -webkit-transform 0.9s cubic-bezier(0.25, 0.8, 0.25, 1); } .md-ripple.md-ripple-scaled { -webkit-transform: translate(-50%, -50%) scale(1); transform: translate(-50%, -50%) scale(1); } .md-ripple.md-ripple-active, .md-ripple.md-ripple-full, .md-ripple.md-ripple-visible { opacity: 0.20; } .md-ripple.md-ripple-remove { -webkit-animation: md-remove-ripple 0.9s cubic-bezier(0.25, 0.8, 0.25, 1); animation: md-remove-ripple 0.9s cubic-bezier(0.25, 0.8, 0.25, 1); } @-webkit-keyframes md-remove-ripple { 0% { opacity: .15; } 100% { opacity: 0; } } @keyframes md-remove-ripple { 0% { opacity: .15; } 100% { opacity: 0; } } .md-padding { padding: 8px; } .md-margin { margin: 8px; } .md-scroll-mask { position: absolute; background-color: transparent; top: 0; right: 0; bottom: 0; left: 0; z-index: 50; } .md-scroll-mask > .md-scroll-mask-bar { display: block; position: absolute; background-color: #fafafa; right: 0; top: 0; bottom: 0; z-index: 65; box-shadow: inset 0px 0px 1px rgba(0, 0, 0, 0.3); } .md-no-momentum { -webkit-overflow-scrolling: auto; } .md-no-flicker { -webkit-filter: blur(0px); } @media (min-width: 960px) { .md-padding { padding: 16px; } } html[dir=rtl], html[dir=ltr], body[dir=rtl], body[dir=ltr] { unicode-bidi: embed; } bdo[dir=rtl] { direction: rtl; unicode-bidi: bidi-override; } bdo[dir=ltr] { direction: ltr; unicode-bidi: bidi-override; } html, body { -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; min-height: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /************ * Headings ************/ .md-display-4 { font-size: 112px; font-weight: 300; letter-spacing: -0.010em; line-height: 112px; } .md-display-3 { font-size: 56px; font-weight: 400; letter-spacing: -0.005em; line-height: 56px; } .md-display-2 { font-size: 45px; font-weight: 400; line-height: 64px; } .md-display-1 { font-size: 34px; font-weight: 400; line-height: 40px; } .md-headline { font-size: 24px; font-weight: 400; line-height: 32px; } .md-title { font-size: 20px; font-weight: 500; letter-spacing: 0.005em; } .md-subhead { font-size: 16px; font-weight: 400; letter-spacing: 0.010em; line-height: 24px; } /************ * Body Copy ************/ .md-body-1 { font-size: 14px; font-weight: 400; letter-spacing: 0.010em; line-height: 20px; } .md-body-2 { font-size: 14px; font-weight: 500; letter-spacing: 0.010em; line-height: 24px; } .md-caption { font-size: 12px; letter-spacing: 0.020em; } .md-button { letter-spacing: 0.010em; } /************ * Defaults ************/ button, select, html, textarea, input { font-family: Roboto, "Helvetica Neue", sans-serif; } select, button, textarea, input { font-size: 100%; } /* * * Responsive attributes * * References: * 1) https://scotch.io/tutorials/a-visual-guide-to-css3-flexbox-properties#flex * 2) https://css-tricks.com/almanac/properties/f/flex/ * 3) https://css-tricks.com/snippets/css/a-guide-to-flexbox/ * 4) https://github.com/philipwalton/flexbugs#3-min-height-on-a-flex-container-wont-apply-to-its-flex-items * 5) http://godban.com.ua/projects/flexgrid * * */ .md-panel-outer-wrapper { height: 100%; left: 0; position: absolute; top: 0; width: 100%; } ._md-panel-hidden { display: none; } ._md-panel-offscreen { left: -9999px; } ._md-panel-fullscreen { border-radius: 0; left: 0; min-height: 100%; min-width: 100%; position: fixed; top: 0; } ._md-panel-shown .md-panel { opacity: 1; -webkit-transition: none; transition: none; } .md-panel { opacity: 0; position: fixed; } .md-panel._md-panel-shown { opacity: 1; -webkit-transition: none; transition: none; } .md-panel._md-panel-animate-enter { opacity: 1; -webkit-transition: all 0.3s cubic-bezier(0, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0, 0, 0.2, 1); } .md-panel._md-panel-animate-leave { opacity: 1; -webkit-transition: all 0.3s cubic-bezier(0.4, 0, 1, 1); transition: all 0.3s cubic-bezier(0.4, 0, 1, 1); } .md-panel._md-panel-animate-scale-out, .md-panel._md-panel-animate-fade-out { opacity: 0; } .md-panel._md-panel-backdrop { height: 100%; position: absolute; width: 100%; } .md-panel._md-opaque-enter { opacity: .48; -webkit-transition: opacity 0.3s cubic-bezier(0, 0, 0.2, 1); transition: opacity 0.3s cubic-bezier(0, 0, 0.2, 1); } .md-panel._md-opaque-leave { -webkit-transition: opacity 0.3s cubic-bezier(0.4, 0, 1, 1); transition: opacity 0.3s cubic-bezier(0.4, 0, 1, 1); } md-autocomplete { border-radius: 2px; display: block; height: 40px; position: relative; overflow: visible; min-width: 190px; } md-autocomplete[disabled] input { cursor: default; } md-autocomplete[md-floating-label] { border-radius: 0; background: transparent; height: auto; } md-autocomplete[md-floating-label] md-input-container { padding-bottom: 0; } md-autocomplete[md-floating-label] md-autocomplete-wrap { height: auto; } md-autocomplete[md-floating-label] .md-show-clear-button button { display: block; position: absolute; right: 0; top: 20px; width: 30px; height: 30px; } md-autocomplete[md-floating-label] .md-show-clear-button input { padding-right: 30px; } [dir=rtl] md-autocomplete[md-floating-label] .md-show-clear-button input { padding-right: 0; padding-left: 30px; } md-autocomplete md-autocomplete-wrap { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; box-sizing: border-box; position: relative; overflow: visible; height: 40px; } md-autocomplete md-autocomplete-wrap.md-menu-showing { z-index: 51; } md-autocomplete md-autocomplete-wrap md-input-container, md-autocomplete md-autocomplete-wrap input { -webkit-box-flex: 1; -webkit-flex: 1 1 0%; flex: 1 1 0%; box-sizing: border-box; min-width: 0; } md-autocomplete md-autocomplete-wrap md-progress-linear { position: absolute; bottom: -2px; left: 0; } md-autocomplete md-autocomplete-wrap md-progress-linear.md-inline { bottom: 40px; right: 2px; left: 2px; width: auto; } md-autocomplete md-autocomplete-wrap md-progress-linear .md-mode-indeterminate { position: absolute; top: 0; left: 0; width: 100%; height: 3px; -webkit-transition: none; transition: none; } md-autocomplete md-autocomplete-wrap md-progress-linear .md-mode-indeterminate .md-container { -webkit-transition: none; transition: none; height: 3px; } md-autocomplete md-autocomplete-wrap md-progress-linear .md-mode-indeterminate.ng-enter { -webkit-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } md-autocomplete md-autocomplete-wrap md-progress-linear .md-mode-indeterminate.ng-enter.ng-enter-active { opacity: 1; } md-autocomplete md-autocomplete-wrap md-progress-linear .md-mode-indeterminate.ng-leave { -webkit-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } md-autocomplete md-autocomplete-wrap md-progress-linear .md-mode-indeterminate.ng-leave.ng-leave-active { opacity: 0; } md-autocomplete input:not(.md-input) { font-size: 14px; box-sizing: border-box; border: none; box-shadow: none; outline: none; background: transparent; width: 100%; padding: 0 15px; line-height: 40px; height: 40px; } md-autocomplete input:not(.md-input)::-ms-clear { display: none; } md-autocomplete .md-show-clear-button button { position: relative; line-height: 20px; text-align: center; width: 30px; height: 30px; cursor: pointer; border: none; border-radius: 50%; padding: 0; font-size: 12px; background: transparent; margin: auto 5px; } md-autocomplete .md-show-clear-button button:after { content: ''; position: absolute; top: -6px; right: -6px; bottom: -6px; left: -6px; border-radius: 50%; -webkit-transform: scale(0); transform: scale(0); opacity: 0; -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); } md-autocomplete .md-show-clear-button button:focus { outline: none; } md-autocomplete .md-show-clear-button button:focus:after { -webkit-transform: scale(1); transform: scale(1); opacity: 1; } md-autocomplete .md-show-clear-button button md-icon { position: absolute; top: 50%; left: 50%; -webkit-transform: translate3d(-50%, -50%, 0) scale(0.9); transform: translate3d(-50%, -50%, 0) scale(0.9); } md-autocomplete .md-show-clear-button button md-icon path { stroke-width: 0; } md-autocomplete .md-show-clear-button button.ng-enter { -webkit-transform: scale(0); transform: scale(0); -webkit-transition: -webkit-transform 0.15s ease-out; transition: -webkit-transform 0.15s ease-out; transition: transform 0.15s ease-out; transition: transform 0.15s ease-out, -webkit-transform 0.15s ease-out; } md-autocomplete .md-show-clear-button button.ng-enter.ng-enter-active { -webkit-transform: scale(1); transform: scale(1); } md-autocomplete .md-show-clear-button button.ng-leave { -webkit-transition: -webkit-transform 0.15s ease-out; transition: -webkit-transform 0.15s ease-out; transition: transform 0.15s ease-out; transition: transform 0.15s ease-out, -webkit-transform 0.15s ease-out; } md-autocomplete .md-show-clear-button button.ng-leave.ng-leave-active { -webkit-transform: scale(0); transform: scale(0); } @media screen and (-ms-high-contrast: active) { md-autocomplete input { border: 1px solid #fff; } md-autocomplete li:focus { color: #fff; } } .md-virtual-repeat-container.md-autocomplete-suggestions-container { position: absolute; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25); z-index: 100; height: 100%; } .md-virtual-repeat-container.md-not-found { height: 48px; } .md-autocomplete-suggestions { margin: 0; list-style: none; padding: 0; } .md-autocomplete-suggestions li { font-size: 14px; overflow: hidden; padding: 0 15px; line-height: 48px; height: 48px; -webkit-transition: background 0.15s linear; transition: background 0.15s linear; margin: 0; white-space: nowrap; text-overflow: ellipsis; } .md-autocomplete-suggestions li:focus { outline: none; } .md-autocomplete-suggestions li:not(.md-not-found-wrapper) { cursor: pointer; } @media screen and (-ms-high-contrast: active) { md-autocomplete, .md-autocomplete-suggestions { border: 1px solid #fff; } } md-backdrop { -webkit-transition: opacity 450ms; transition: opacity 450ms; position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: 50; } md-backdrop.md-menu-backdrop { position: fixed !important; z-index: 99; } md-backdrop.md-select-backdrop { z-index: 81; -webkit-transition-duration: 0; transition-duration: 0; } md-backdrop.md-dialog-backdrop { z-index: 79; } md-backdrop.md-bottom-sheet-backdrop { z-index: 69; } md-backdrop.md-sidenav-backdrop { z-index: 59; } md-backdrop.md-click-catcher { position: absolute; } md-backdrop.md-opaque { opacity: .48; } md-backdrop.md-opaque.ng-enter { opacity: 0; } md-backdrop.md-opaque.ng-enter.md-opaque.ng-enter-active { opacity: .48; } md-backdrop.md-opaque.ng-leave { opacity: .48; -webkit-transition: opacity 400ms; transition: opacity 400ms; } md-backdrop.md-opaque.ng-leave.md-opaque.ng-leave-active { opacity: 0; } md-bottom-sheet { position: absolute; left: 0; right: 0; bottom: 0; padding: 8px 16px 88px 16px; z-index: 70; border-top-width: 1px; border-top-style: solid; -webkit-transform: translate3d(0, 80px, 0); transform: translate3d(0, 80px, 0); -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transition-property: -webkit-transform; transition-property: -webkit-transform; transition-property: transform; transition-property: transform, -webkit-transform; } md-bottom-sheet.md-has-header { padding-top: 0; } md-bottom-sheet.ng-enter { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } md-bottom-sheet.ng-enter-active { opacity: 1; display: block; -webkit-transform: translate3d(0, 80px, 0) !important; transform: translate3d(0, 80px, 0) !important; } md-bottom-sheet.ng-leave-active { -webkit-transform: translate3d(0, 100%, 0) !important; transform: translate3d(0, 100%, 0) !important; -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); } md-bottom-sheet .md-subheader { background-color: transparent; font-family: Roboto, "Helvetica Neue", sans-serif; line-height: 56px; padding: 0; white-space: nowrap; } md-bottom-sheet md-inline-icon { display: inline-block; height: 24px; width: 24px; fill: #444; } md-bottom-sheet md-list-item { display: -webkit-box; display: -webkit-flex; display: flex; outline: none; } md-bottom-sheet md-list-item:hover { cursor: pointer; } md-bottom-sheet.md-list md-list-item { padding: 0; -webkit-box-align: center; -webkit-align-items: center; align-items: center; height: 48px; } md-bottom-sheet.md-grid { padding-left: 24px; padding-right: 24px; padding-top: 0; } md-bottom-sheet.md-grid md-list { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; -webkit-flex-wrap: wrap; flex-wrap: wrap; -webkit-transition: all 0.5s; transition: all 0.5s; -webkit-box-align: center; -webkit-align-items: center; align-items: center; } md-bottom-sheet.md-grid md-list-item { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; -webkit-box-align: center; -webkit-align-items: center; align-items: center; -webkit-transition: all 0.5s; transition: all 0.5s; height: 96px; margin-top: 8px; margin-bottom: 8px; /* Mixin for how many grid items to show per row */ } @media (max-width: 960px) { md-bottom-sheet.md-grid md-list-item { -webkit-box-flex: 1; -webkit-flex: 1 1 33.33333%; flex: 1 1 33.33333%; max-width: 33.33333%; } md-bottom-sheet.md-grid md-list-item:nth-of-type(3n + 1) { -webkit-box-align: start; -webkit-align-items: flex-start; align-items: flex-start; } md-bottom-sheet.md-grid md-list-item:nth-of-type(3n) { -webkit-box-align: end; -webkit-align-items: flex-end; align-items: flex-end; } } @media (min-width: 960px) and (max-width: 1279px) { md-bottom-sheet.md-grid md-list-item { -webkit-box-flex: 1; -webkit-flex: 1 1 25%; flex: 1 1 25%; max-width: 25%; } } @media (min-width: 1280px) and (max-width: 1919px) { md-bottom-sheet.md-grid md-list-item { -webkit-box-flex: 1; -webkit-flex: 1 1 16.66667%; flex: 1 1 16.66667%; max-width: 16.66667%; } } @media (min-width: 1920px) { md-bottom-sheet.md-grid md-list-item { -webkit-box-flex: 1; -webkit-flex: 1 1 14.28571%; flex: 1 1 14.28571%; max-width: 14.28571%; } } md-bottom-sheet.md-grid md-list-item::before { display: none; } md-bottom-sheet.md-grid md-list-item .md-list-item-content { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; -webkit-box-align: center; -webkit-align-items: center; align-items: center; width: 48px; padding-bottom: 16px; } md-bottom-sheet.md-grid md-list-item .md-grid-item-content { border: 1px solid transparent; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; -webkit-box-align: center; -webkit-align-items: center; align-items: center; width: 80px; } md-bottom-sheet.md-grid md-list-item .md-grid-text { font-weight: 400; line-height: 16px; font-size: 13px; margin: 0; white-space: nowrap; width: 64px; text-align: center; text-transform: none; padding-top: 8px; } @media screen and (-ms-high-contrast: active) { md-bottom-sheet { border: 1px solid #fff; } } button.md-button::-moz-focus-inner { border: 0; } .md-button { display: inline-block; position: relative; cursor: pointer; /** Alignment adjustments */ min-height: 36px; min-width: 88px; line-height: 36px; vertical-align: middle; -webkit-box-align: center; -webkit-align-items: center; align-items: center; text-align: center; border-radius: 2px; box-sizing: border-box; /* Reset default button appearance */ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; outline: none; border: 0; /** Custom styling for button */ padding: 0 6px; margin: 6px 8px; background: transparent; color: currentColor; white-space: nowrap; /* Uppercase text content */ text-transform: uppercase; font-weight: 500; font-size: 14px; font-style: inherit; font-variant: inherit; font-family: inherit; text-decoration: none; overflow: hidden; -webkit-transition: box-shadow 0.4s cubic-bezier(0.25, 0.8, 0.25, 1), background-color 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: box-shadow 0.4s cubic-bezier(0.25, 0.8, 0.25, 1), background-color 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); } .md-dense > .md-button:not(.md-dense-disabled), .md-dense :not(.md-dense-disabled) .md-button:not(.md-dense-disabled) { min-height: 32px; } .md-dense > .md-button:not(.md-dense-disabled), .md-dense :not(.md-dense-disabled) .md-button:not(.md-dense-disabled) { line-height: 32px; } .md-dense > .md-button:not(.md-dense-disabled), .md-dense :not(.md-dense-disabled) .md-button:not(.md-dense-disabled) { font-size: 13px; } .md-button:focus { outline: none; } .md-button:hover, .md-button:focus { text-decoration: none; } .md-button.ng-hide, .md-button.ng-leave { -webkit-transition: none; transition: none; } .md-button.md-cornered { border-radius: 0; } .md-button.md-icon { padding: 0; background: none; } .md-button.md-raised:not([disabled]) { box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); } .md-button.md-icon-button { margin: 0 6px; height: 40px; min-width: 0; line-height: 24px; padding: 8px; width: 40px; border-radius: 50%; } .md-button.md-icon-button .md-ripple-container { border-radius: 50%; background-clip: padding-box; overflow: hidden; -webkit-mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC"); } .md-button.md-fab { z-index: 20; line-height: 56px; min-width: 0; width: 56px; height: 56px; vertical-align: middle; box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); border-radius: 50%; background-clip: padding-box; overflow: hidden; -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); -webkit-transition-property: background-color, box-shadow, -webkit-transform; transition-property: background-color, box-shadow, -webkit-transform; transition-property: background-color, box-shadow, transform; transition-property: background-color, box-shadow, transform, -webkit-transform; } .md-button.md-fab.md-fab-bottom-right { top: auto; right: 20px; bottom: 20px; left: auto; position: absolute; } .md-button.md-fab.md-fab-bottom-left { top: auto; right: auto; bottom: 20px; left: 20px; position: absolute; } .md-button.md-fab.md-fab-top-right { top: 20px; right: 20px; bottom: auto; left: auto; position: absolute; } .md-button.md-fab.md-fab-top-left { top: 20px; right: auto; bottom: auto; left: 20px; position: absolute; } .md-button.md-fab .md-ripple-container { border-radius: 50%; background-clip: padding-box; overflow: hidden; -webkit-mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC"); } .md-button.md-fab.md-mini { line-height: 40px; width: 40px; height: 40px; } .md-button.md-fab.ng-hide, .md-button.md-fab.ng-leave { -webkit-transition: none; transition: none; } .md-button:not([disabled]).md-raised.md-focused, .md-button:not([disabled]).md-fab.md-focused { box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); } .md-button:not([disabled]).md-raised:active, .md-button:not([disabled]).md-fab:active { box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4); } .md-button .md-ripple-container { border-radius: 2px; background-clip: padding-box; overflow: hidden; -webkit-mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC"); } .md-button.md-icon-button md-icon, button.md-button.md-fab md-icon { display: block; } .md-toast-open-top .md-button.md-fab-top-left, .md-toast-open-top .md-button.md-fab-top-right { -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transform: translate3d(0, 42px, 0); transform: translate3d(0, 42px, 0); } .md-toast-open-top .md-button.md-fab-top-left:not([disabled]).md-focused, .md-toast-open-top .md-button.md-fab-top-left:not([disabled]):hover, .md-toast-open-top .md-button.md-fab-top-right:not([disabled]).md-focused, .md-toast-open-top .md-button.md-fab-top-right:not([disabled]):hover { -webkit-transform: translate3d(0, 41px, 0); transform: translate3d(0, 41px, 0); } .md-toast-open-bottom .md-button.md-fab-bottom-left, .md-toast-open-bottom .md-button.md-fab-bottom-right { -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transform: translate3d(0, -42px, 0); transform: translate3d(0, -42px, 0); } .md-toast-open-bottom .md-button.md-fab-bottom-left:not([disabled]).md-focused, .md-toast-open-bottom .md-button.md-fab-bottom-left:not([disabled]):hover, .md-toast-open-bottom .md-button.md-fab-bottom-right:not([disabled]).md-focused, .md-toast-open-bottom .md-button.md-fab-bottom-right:not([disabled]):hover { -webkit-transform: translate3d(0, -43px, 0); transform: translate3d(0, -43px, 0); } .md-button-group { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; width: 100%; } .md-button-group > .md-button { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; display: block; overflow: hidden; width: 0; border-width: 1px 0px 1px 1px; border-radius: 0; text-align: center; text-overflow: ellipsis; white-space: nowrap; } .md-button-group > .md-button:first-child { border-radius: 2px 0px 0px 2px; } .md-button-group > .md-button:last-child { border-right-width: 1px; border-radius: 0px 2px 2px 0px; } @media screen and (-ms-high-contrast: active) { .md-button.md-raised, .md-button.md-fab { border: 1px solid #fff; } } md-card { box-sizing: border-box; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; margin: 8px; box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 2px 1px -1px rgba(0, 0, 0, 0.12); } md-card md-card-header { padding: 16px; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-card md-card-header:first-child md-card-avatar { margin-right: 12px; } [dir=rtl] md-card md-card-header:first-child md-card-avatar { margin-right: auto; margin-left: 12px; } md-card md-card-header:last-child md-card-avatar { margin-left: 12px; } [dir=rtl] md-card md-card-header:last-child md-card-avatar { margin-left: auto; margin-right: 12px; } md-card md-card-header md-card-avatar { width: 40px; height: 40px; } md-card md-card-header md-card-avatar .md-user-avatar, md-card md-card-header md-card-avatar md-icon { border-radius: 50%; } md-card md-card-header md-card-avatar md-icon { padding: 8px; } md-card md-card-header md-card-avatar md-icon > svg { height: inherit; width: inherit; } md-card md-card-header md-card-avatar + md-card-header-text { max-height: 40px; } md-card md-card-header md-card-avatar + md-card-header-text .md-title { font-size: 14px; } md-card md-card-header md-card-header-text { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; } md-card md-card-header md-card-header-text .md-subhead { font-size: 14px; } md-card > img, md-card > md-card-header img, md-card md-card-title-media img { box-sizing: border-box; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-flex: 0; -webkit-flex: 0 0 auto; flex: 0 0 auto; width: 100%; height: auto; } md-card md-card-title { padding: 24px 16px 16px; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-flex: 1; -webkit-flex: 1 1 auto; flex: 1 1 auto; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-card md-card-title + md-card-content { padding-top: 0; } md-card md-card-title md-card-title-text { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; display: -webkit-box; display: -webkit-flex; display: flex; } md-card md-card-title md-card-title-text .md-subhead { padding-top: 0; font-size: 14px; } md-card md-card-title md-card-title-text:only-child .md-subhead { padding-top: 12px; } md-card md-card-title md-card-title-media { margin-top: -8px; } md-card md-card-title md-card-title-media .md-media-sm { height: 80px; width: 80px; } md-card md-card-title md-card-title-media .md-media-md { height: 112px; width: 112px; } md-card md-card-title md-card-title-media .md-media-lg { height: 152px; width: 152px; } md-card md-card-content { display: block; padding: 16px; } md-card md-card-content > p:first-child { margin-top: 0; } md-card md-card-content > p:last-child { margin-bottom: 0; } md-card md-card-content .md-media-xl { height: 240px; width: 240px; } md-card .md-actions, md-card md-card-actions { margin: 8px; } md-card .md-actions.layout-column .md-button:not(.md-icon-button), md-card md-card-actions.layout-column .md-button:not(.md-icon-button) { margin: 2px 0; } md-card .md-actions.layout-column .md-button:not(.md-icon-button):first-of-type, md-card md-card-actions.layout-column .md-button:not(.md-icon-button):first-of-type { margin-top: 0; } md-card .md-actions.layout-column .md-button:not(.md-icon-button):last-of-type, md-card md-card-actions.layout-column .md-button:not(.md-icon-button):last-of-type { margin-bottom: 0; } md-card .md-actions.layout-column .md-button.md-icon-button, md-card md-card-actions.layout-column .md-button.md-icon-button { margin-top: 6px; margin-bottom: 6px; } md-card .md-actions md-card-icon-actions, md-card md-card-actions md-card-icon-actions { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; -webkit-box-pack: start; -webkit-justify-content: flex-start; justify-content: flex-start; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button), md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button) { margin: 0 4px; } md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type, md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type { margin-left: 0; } [dir=rtl] md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):first-of-type { margin-left: auto; margin-right: 0; } md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type, md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type { margin-right: 0; } [dir=rtl] md-card .md-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button:not(.md-icon-button):last-of-type { margin-right: auto; margin-left: 0; } md-card .md-actions:not(.layout-column) .md-button.md-icon-button, md-card md-card-actions:not(.layout-column) .md-button.md-icon-button { margin-left: 6px; margin-right: 6px; } md-card .md-actions:not(.layout-column) .md-button.md-icon-button:first-of-type, md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:first-of-type { margin-left: 12px; } [dir=rtl] md-card .md-actions:not(.layout-column) .md-button.md-icon-button:first-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:first-of-type { margin-left: auto; margin-right: 12px; } md-card .md-actions:not(.layout-column) .md-button.md-icon-button:last-of-type, md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:last-of-type { margin-right: 12px; } [dir=rtl] md-card .md-actions:not(.layout-column) .md-button.md-icon-button:last-of-type, [dir=rtl] md-card md-card-actions:not(.layout-column) .md-button.md-icon-button:last-of-type { margin-right: auto; margin-left: 12px; } md-card .md-actions:not(.layout-column) .md-button + md-card-icon-actions, md-card md-card-actions:not(.layout-column) .md-button + md-card-icon-actions { -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; -webkit-box-pack: end; -webkit-justify-content: flex-end; justify-content: flex-end; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-card md-card-footer { margin-top: auto; padding: 16px; } @media screen and (-ms-high-contrast: active) { md-card { border: 1px solid #fff; } } .md-image-no-fill > img { width: auto; height: auto; } .md-contact-chips .md-chips md-chip { padding: 0 25px 0 0; } [dir=rtl] .md-contact-chips .md-chips md-chip { padding: 0 0 0 25px; } .md-contact-chips .md-chips md-chip .md-contact-avatar { float: left; } [dir=rtl] .md-contact-chips .md-chips md-chip .md-contact-avatar { float: right; } .md-contact-chips .md-chips md-chip .md-contact-avatar img { height: 32px; border-radius: 16px; } .md-contact-chips .md-chips md-chip .md-contact-name { display: inline-block; height: 32px; margin-left: 8px; } [dir=rtl] .md-contact-chips .md-chips md-chip .md-contact-name { margin-left: auto; margin-right: 8px; } .md-contact-suggestion { height: 56px; } .md-contact-suggestion img { height: 40px; border-radius: 20px; margin-top: 8px; } .md-contact-suggestion .md-contact-name { margin-left: 8px; width: 120px; } [dir=rtl] .md-contact-suggestion .md-contact-name { margin-left: auto; margin-right: 8px; } .md-contact-suggestion .md-contact-name, .md-contact-suggestion .md-contact-email { display: inline-block; overflow: hidden; text-overflow: ellipsis; } .md-contact-chips-suggestions li { height: 100%; } .md-chips { display: block; font-family: Roboto, "Helvetica Neue", sans-serif; font-size: 16px; padding: 0 0 8px 3px; vertical-align: middle; } .md-chips:after { content: ''; display: table; clear: both; } [dir=rtl] .md-chips { padding: 0 3px 8px 0; } .md-chips.md-readonly .md-chip-input-container { min-height: 32px; } .md-chips:not(.md-readonly) { cursor: text; } .md-chips.md-removable md-chip { padding-right: 22px; } [dir=rtl] .md-chips.md-removable md-chip { padding-right: 0; padding-left: 22px; } .md-chips.md-removable md-chip .md-chip-content { padding-right: 4px; } [dir=rtl] .md-chips.md-removable md-chip .md-chip-content { padding-right: 0; padding-left: 4px; } .md-chips md-chip { cursor: default; border-radius: 16px; display: block; height: 32px; line-height: 32px; margin: 8px 8px 0 0; padding: 0 12px 0 12px; float: left; box-sizing: border-box; max-width: 100%; position: relative; } [dir=rtl] .md-chips md-chip { margin: 8px 0 0 8px; } [dir=rtl] .md-chips md-chip { float: right; } .md-chips md-chip .md-chip-content { display: block; float: left; white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; } [dir=rtl] .md-chips md-chip .md-chip-content { float: right; } .md-chips md-chip .md-chip-content:focus { outline: none; } .md-chips md-chip._md-chip-content-edit-is-enabled { -webkit-user-select: none; /* webkit (safari, chrome) browsers */ -moz-user-select: none; /* mozilla browsers */ -khtml-user-select: none; /* webkit (konqueror) browsers */ -ms-user-select: none; /* IE10+ */ } .md-chips md-chip .md-chip-remove-container { position: absolute; right: 0; line-height: 22px; } [dir=rtl] .md-chips md-chip .md-chip-remove-container { right: auto; left: 0; } .md-chips md-chip .md-chip-remove { text-align: center; width: 32px; height: 32px; min-width: 0; padding: 0; background: transparent; border: none; box-shadow: none; margin: 0; position: relative; } .md-chips md-chip .md-chip-remove md-icon { height: 18px; width: 18px; position: absolute; top: 50%; left: 50%; -webkit-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } .md-chips .md-chip-input-container { display: block; line-height: 32px; margin: 8px 8px 0 0; padding: 0; float: left; } [dir=rtl] .md-chips .md-chip-input-container { margin: 8px 0 0 8px; } [dir=rtl] .md-chips .md-chip-input-container { float: right; } .md-chips .md-chip-input-container input:not([type]), .md-chips .md-chip-input-container input[type="email"], .md-chips .md-chip-input-container input[type="number"], .md-chips .md-chip-input-container input[type="tel"], .md-chips .md-chip-input-container input[type="url"], .md-chips .md-chip-input-container input[type="text"] { border: 0; height: 32px; line-height: 32px; padding: 0; } .md-chips .md-chip-input-container input:not([type]):focus, .md-chips .md-chip-input-container input[type="email"]:focus, .md-chips .md-chip-input-container input[type="number"]:focus, .md-chips .md-chip-input-container input[type="tel"]:focus, .md-chips .md-chip-input-container input[type="url"]:focus, .md-chips .md-chip-input-container input[type="text"]:focus { outline: none; } .md-chips .md-chip-input-container md-autocomplete, .md-chips .md-chip-input-container md-autocomplete-wrap { background: transparent; height: 32px; } .md-chips .md-chip-input-container md-autocomplete md-autocomplete-wrap { box-shadow: none; } .md-chips .md-chip-input-container md-autocomplete input { position: relative; } .md-chips .md-chip-input-container input { border: 0; height: 32px; line-height: 32px; padding: 0; } .md-chips .md-chip-input-container input:focus { outline: none; } .md-chips .md-chip-input-container md-autocomplete, .md-chips .md-chip-input-container md-autocomplete-wrap { height: 32px; } .md-chips .md-chip-input-container md-autocomplete { box-shadow: none; } .md-chips .md-chip-input-container md-autocomplete input { position: relative; } .md-chips .md-chip-input-container:not(:first-child) { margin: 8px 8px 0 0; } [dir=rtl] .md-chips .md-chip-input-container:not(:first-child) { margin: 8px 0 0 8px; } .md-chips .md-chip-input-container input { background: transparent; border-width: 0; } .md-chips md-autocomplete button { display: none; } @media screen and (-ms-high-contrast: active) { .md-chip-input-container, md-chip { border: 1px solid #fff; } .md-chip-input-container md-autocomplete { border: none; } } .md-inline-form md-checkbox { margin: 19px 0 18px; } md-checkbox { box-sizing: border-box; display: inline-block; margin-bottom: 16px; white-space: nowrap; cursor: pointer; outline: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; position: relative; min-width: 20px; min-height: 20px; margin-left: 0; margin-right: 16px; } [dir=rtl] md-checkbox { margin-left: 16px; } [dir=rtl] md-checkbox { margin-right: 0; } md-checkbox:last-of-type { margin-left: 0; margin-right: 0; } md-checkbox.md-focused:not([disabled]) .md-container:before { left: -8px; top: -8px; right: -8px; bottom: -8px; } md-checkbox.md-focused:not([disabled]):not(.md-checked) .md-container:before { background-color: rgba(0, 0, 0, 0.12); } md-checkbox.md-align-top-left > div.md-container { top: 12px; } md-checkbox .md-container { position: absolute; top: 50%; -webkit-transform: translateY(-50%); transform: translateY(-50%); box-sizing: border-box; display: inline-block; width: 20px; height: 20px; left: 0; right: auto; } [dir=rtl] md-checkbox .md-container { left: auto; } [dir=rtl] md-checkbox .md-container { right: 0; } md-checkbox .md-container:before { box-sizing: border-box; background-color: transparent; border-radius: 50%; content: ''; position: absolute; display: block; height: auto; left: 0; top: 0; right: 0; bottom: 0; -webkit-transition: all 0.5s; transition: all 0.5s; width: auto; } md-checkbox .md-container:after { box-sizing: border-box; content: ''; position: absolute; top: -10px; right: -10px; bottom: -10px; left: -10px; } md-checkbox .md-container .md-ripple-container { position: absolute; display: block; width: auto; height: auto; left: -15px; top: -15px; right: -15px; bottom: -15px; } md-checkbox .md-icon { box-sizing: border-box; -webkit-transition: 240ms; transition: 240ms; position: absolute; top: 0; left: 0; width: 20px; height: 20px; border-width: 2px; border-style: solid; border-radius: 2px; } md-checkbox.md-checked .md-icon { border-color: transparent; } md-checkbox.md-checked .md-icon:after { box-sizing: border-box; -webkit-transform: rotate(45deg); transform: rotate(45deg); position: absolute; left: 4.66667px; top: 0.22222px; display: table; width: 6.66667px; height: 13.33333px; border-width: 2px; border-style: solid; border-top: 0; border-left: 0; content: ''; } md-checkbox[disabled] { cursor: default; } md-checkbox.md-indeterminate .md-icon:after { box-sizing: border-box; position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); display: table; width: 12px; height: 2px; border-width: 2px; border-style: solid; border-top: 0; border-left: 0; content: ''; } md-checkbox .md-label { box-sizing: border-box; position: relative; display: inline-block; vertical-align: middle; white-space: normal; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; margin-left: 30px; margin-right: 0; } [dir=rtl] md-checkbox .md-label { margin-left: 0; } [dir=rtl] md-checkbox .md-label { margin-right: 30px; } md-content { display: block; position: relative; overflow: auto; -webkit-overflow-scrolling: touch; } md-content[md-scroll-y] { overflow-y: auto; overflow-x: hidden; } md-content[md-scroll-x] { overflow-x: auto; overflow-y: hidden; } @media print { md-content { overflow: visible !important; } } /** Styles for mdCalendar. */ md-calendar { font-size: 13px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .md-calendar-scroll-mask { display: inline-block; overflow: hidden; height: 308px; } .md-calendar-scroll-mask .md-virtual-repeat-scroller { overflow-y: scroll; -webkit-overflow-scrolling: touch; } .md-calendar-scroll-mask .md-virtual-repeat-scroller::-webkit-scrollbar { display: none; } .md-calendar-scroll-mask .md-virtual-repeat-offsetter { width: 100%; } .md-calendar-scroll-container { box-shadow: inset -3px 3px 6px rgba(0, 0, 0, 0.2); display: inline-block; height: 308px; width: 346px; } .md-calendar-date { height: 44px; width: 44px; text-align: center; padding: 0; border: none; box-sizing: content-box; } .md-calendar-date:first-child { padding-left: 16px; } [dir=rtl] .md-calendar-date:first-child { padding-left: 0; padding-right: 16px; } .md-calendar-date:last-child { padding-right: 16px; } [dir=rtl] .md-calendar-date:last-child { padding-right: 0; padding-left: 16px; } .md-calendar-date.md-calendar-date-disabled { cursor: default; } .md-calendar-date-selection-indicator { -webkit-transition: background-color, color 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: background-color, color 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); border-radius: 50%; display: inline-block; width: 40px; height: 40px; line-height: 40px; } .md-calendar-date:not(.md-disabled) .md-calendar-date-selection-indicator { cursor: pointer; } .md-calendar-month-label { height: 44px; font-size: 14px; font-weight: 500; padding: 0 0 0 24px; } [dir=rtl] .md-calendar-month-label { padding: 0 24px 0 0; } md-calendar-month .md-calendar-month-label:not(.md-calendar-month-label-disabled) { cursor: pointer; } .md-calendar-month-label md-icon { -webkit-transform: rotate(180deg); transform: rotate(180deg); } [dir=rtl] .md-calendar-month-label md-icon { -webkit-transform: none; transform: none; } .md-calendar-month-label span { vertical-align: middle; } .md-calendar-day-header { table-layout: fixed; border-spacing: 0; border-collapse: collapse; } .md-calendar-day-header th { height: 40px; width: 44px; text-align: center; padding: 0; border: none; box-sizing: content-box; font-weight: normal; } .md-calendar-day-header th:first-child { padding-left: 16px; } [dir=rtl] .md-calendar-day-header th:first-child { padding-left: 0; padding-right: 16px; } .md-calendar-day-header th:last-child { padding-right: 16px; } [dir=rtl] .md-calendar-day-header th:last-child { padding-right: 0; padding-left: 16px; } .md-calendar { table-layout: fixed; border-spacing: 0; border-collapse: collapse; } .md-calendar tr:last-child td { border-bottom-width: 1px; border-bottom-style: solid; } .md-calendar:first-child { border-top: 1px solid transparent; } .md-calendar tbody, .md-calendar td, .md-calendar tr { vertical-align: middle; box-sizing: content-box; } /** Styles for mdDatepicker. */ md-datepicker { white-space: nowrap; overflow: hidden; vertical-align: middle; } .md-inline-form md-datepicker { margin-top: 12px; } .md-datepicker-button { display: inline-block; box-sizing: border-box; background: none; vertical-align: middle; position: relative; } .md-datepicker-button:before { top: 0; left: 0; bottom: 0; right: 0; position: absolute; content: ''; speak: none; } .md-datepicker-input { font-size: 14px; box-sizing: border-box; border: none; box-shadow: none; outline: none; background: transparent; min-width: 120px; max-width: 328px; padding: 0 0 5px; } .md-datepicker-input::-ms-clear { display: none; } ._md-datepicker-floating-label > md-datepicker { overflow: visible; } ._md-datepicker-floating-label > md-datepicker .md-datepicker-input-container { border: none; } ._md-datepicker-floating-label > md-datepicker .md-datepicker-button { float: left; margin-top: -12px; top: 9.5px; } [dir=rtl] ._md-datepicker-floating-label > md-datepicker .md-datepicker-button { float: right; } ._md-datepicker-floating-label .md-input { float: none; } ._md-datepicker-floating-label._md-datepicker-has-calendar-icon > label:not(.md-no-float):not(.md-container-ignore) { right: 18px; left: auto; width: calc(100% - 84px); } [dir=rtl] ._md-datepicker-floating-label._md-datepicker-has-calendar-icon > label:not(.md-no-float):not(.md-container-ignore) { right: auto; } [dir=rtl] ._md-datepicker-floating-label._md-datepicker-has-calendar-icon > label:not(.md-no-float):not(.md-container-ignore) { left: 18px; } ._md-datepicker-floating-label._md-datepicker-has-calendar-icon .md-input-message-animation { margin-left: 64px; } [dir=rtl] ._md-datepicker-floating-label._md-datepicker-has-calendar-icon .md-input-message-animation { margin-left: auto; margin-right: 64px; } ._md-datepicker-has-triangle-icon { padding-right: 18px; margin-right: -18px; } [dir=rtl] ._md-datepicker-has-triangle-icon { padding-right: 0; padding-left: 18px; } [dir=rtl] ._md-datepicker-has-triangle-icon { margin-right: auto; margin-left: -18px; } .md-datepicker-input-container { position: relative; border-bottom-width: 1px; border-bottom-style: solid; display: inline-block; width: auto; } .md-icon-button + .md-datepicker-input-container { margin-left: 12px; } [dir=rtl] .md-icon-button + .md-datepicker-input-container { margin-left: auto; margin-right: 12px; } .md-datepicker-input-container.md-datepicker-focused { border-bottom-width: 2px; } .md-datepicker-is-showing .md-scroll-mask { z-index: 99; } .md-datepicker-calendar-pane { position: absolute; top: 0; left: -100%; z-index: 100; border-width: 1px; border-style: solid; background: transparent; -webkit-transform: scale(0); transform: scale(0); -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transition: -webkit-transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); transition: -webkit-transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1), -webkit-transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); } .md-datepicker-calendar-pane.md-pane-open { -webkit-transform: scale(1); transform: scale(1); } .md-datepicker-input-mask { height: 40px; width: 340px; position: relative; overflow: hidden; background: transparent; pointer-events: none; cursor: text; } .md-datepicker-calendar { opacity: 0; -webkit-transition: opacity 0.2s cubic-bezier(0.5, 0, 0.25, 1); transition: opacity 0.2s cubic-bezier(0.5, 0, 0.25, 1); } .md-pane-open .md-datepicker-calendar { opacity: 1; } .md-datepicker-calendar md-calendar:focus { outline: none; } .md-datepicker-expand-triangle { position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid; } .md-datepicker-triangle-button { position: absolute; right: 0; bottom: -2.5px; -webkit-transform: translateX(45%); transform: translateX(45%); } [dir=rtl] .md-datepicker-triangle-button { right: auto; left: 0; } [dir=rtl] .md-datepicker-triangle-button { -webkit-transform: translateX(-45%); transform: translateX(-45%); } .md-datepicker-triangle-button.md-button.md-icon-button { height: 36px; width: 36px; position: absolute; padding: 8px; } md-datepicker[disabled] .md-datepicker-input-container { border-bottom-color: transparent; } md-datepicker[disabled] .md-datepicker-triangle-button { display: none; } .md-datepicker-open { overflow: hidden; } .md-datepicker-open .md-datepicker-input-container, .md-datepicker-open input.md-input { border-bottom-color: transparent; } .md-datepicker-open .md-datepicker-triangle-button, .md-datepicker-open.md-input-has-value > label, .md-datepicker-open.md-input-has-placeholder > label { display: none; } .md-datepicker-pos-adjusted .md-datepicker-input-mask { display: none; } .md-datepicker-calendar-pane .md-calendar { -webkit-transform: translateY(-85px); transform: translateY(-85px); -webkit-transition: -webkit-transform 0.65s cubic-bezier(0.25, 0.8, 0.25, 1); transition: -webkit-transform 0.65s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.65s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.65s cubic-bezier(0.25, 0.8, 0.25, 1), -webkit-transform 0.65s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transition-delay: 0.125s; transition-delay: 0.125s; } .md-datepicker-calendar-pane.md-pane-open .md-calendar { -webkit-transform: translateY(0); transform: translateY(0); } .md-dialog-is-showing { max-height: 100%; } .md-dialog-container { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-pack: center; -webkit-justify-content: center; justify-content: center; -webkit-box-align: center; -webkit-align-items: center; align-items: center; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 80; overflow: hidden; } md-dialog { opacity: 0; min-width: 240px; max-width: 80%; max-height: 80%; position: relative; overflow: auto; box-shadow: 0px 7px 8px -4px rgba(0, 0, 0, 0.2), 0px 13px 19px 2px rgba(0, 0, 0, 0.14), 0px 5px 24px 4px rgba(0, 0, 0, 0.12); display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; } md-dialog.md-transition-in { opacity: 1; -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transform: translate(0, 0) scale(1); transform: translate(0, 0) scale(1); } md-dialog.md-transition-out { opacity: 0; -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transform: translate(0, 100%) scale(0.2); transform: translate(0, 100%) scale(0.2); } md-dialog > form { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; overflow: auto; } md-dialog .md-dialog-content { padding: 24px; } md-dialog md-dialog-content { -webkit-box-ordinal-group: 2; -webkit-order: 1; order: 1; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; overflow: auto; -webkit-overflow-scrolling: touch; } md-dialog md-dialog-content:not([layout=row]) > *:first-child:not(.md-subheader) { margin-top: 0; } md-dialog md-dialog-content:focus { outline: none; } md-dialog md-dialog-content .md-subheader { margin: 0; } md-dialog md-dialog-content .md-dialog-content-body { width: 100%; } md-dialog md-dialog-content .md-prompt-input-container { width: 100%; box-sizing: border-box; } md-dialog .md-actions, md-dialog md-dialog-actions { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-ordinal-group: 3; -webkit-order: 2; order: 2; box-sizing: border-box; -webkit-box-align: center; -webkit-align-items: center; align-items: center; -webkit-box-pack: end; -webkit-justify-content: flex-end; justify-content: flex-end; margin-bottom: 0; padding-right: 8px; padding-left: 16px; min-height: 52px; overflow: hidden; } [dir=rtl] md-dialog .md-actions, [dir=rtl] md-dialog md-dialog-actions { padding-right: 16px; } [dir=rtl] md-dialog .md-actions, [dir=rtl] md-dialog md-dialog-actions { padding-left: 8px; } md-dialog .md-actions .md-button, md-dialog md-dialog-actions .md-button { margin-bottom: 8px; margin-left: 8px; margin-right: 0; margin-top: 8px; } [dir=rtl] md-dialog .md-actions .md-button, [dir=rtl] md-dialog md-dialog-actions .md-button { margin-left: 0; } [dir=rtl] md-dialog .md-actions .md-button, [dir=rtl] md-dialog md-dialog-actions .md-button { margin-right: 8px; } md-dialog.md-content-overflow .md-actions, md-dialog.md-content-overflow md-dialog-actions { border-top-width: 1px; border-top-style: solid; } @media screen and (-ms-high-contrast: active) { md-dialog { border: 1px solid #fff; } } @media (max-width: 959px) { md-dialog.md-dialog-fullscreen { min-height: 100%; min-width: 100%; border-radius: 0; } } md-divider { display: block; border-top-width: 1px; border-top-style: solid; margin: 0; } md-divider[md-inset] { margin-left: 80px; } [dir=rtl] md-divider[md-inset] { margin-left: auto; margin-right: 80px; } .layout-row > md-divider, .layout-xs-row > md-divider, .layout-gt-xs-row > md-divider, .layout-sm-row > md-divider, .layout-gt-sm-row > md-divider, .layout-md-row > md-divider, .layout-gt-md-row > md-divider, .layout-lg-row > md-divider, .layout-gt-lg-row > md-divider, .layout-xl-row > md-divider { border-top-width: 0; border-right-width: 1px; border-right-style: solid; } md-fab-speed-dial { position: relative; display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; z-index: 20; /* * Hide some graphics glitches if switching animation types */ /* * Handle the animations */ } md-fab-speed-dial.md-fab-bottom-right { top: auto; right: 20px; bottom: 20px; left: auto; position: absolute; } md-fab-speed-dial.md-fab-bottom-left { top: auto; right: auto; bottom: 20px; left: 20px; position: absolute; } md-fab-speed-dial.md-fab-top-right { top: 20px; right: 20px; bottom: auto; left: auto; position: absolute; } md-fab-speed-dial.md-fab-top-left { top: 20px; right: auto; bottom: auto; left: 20px; position: absolute; } md-fab-speed-dial:not(.md-hover-full) { pointer-events: none; } md-fab-speed-dial:not(.md-hover-full) md-fab-trigger, md-fab-speed-dial:not(.md-hover-full) .md-fab-action-item { pointer-events: auto; } md-fab-speed-dial:not(.md-hover-full).md-is-open { pointer-events: auto; } md-fab-speed-dial ._md-css-variables { z-index: 20; } md-fab-speed-dial.md-is-open .md-fab-action-item { -webkit-box-align: center; -webkit-align-items: center; align-items: center; } md-fab-speed-dial md-fab-actions { display: -webkit-box; display: -webkit-flex; display: flex; height: auto; } md-fab-speed-dial md-fab-actions .md-fab-action-item { -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); } md-fab-speed-dial.md-down { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; } md-fab-speed-dial.md-down md-fab-trigger { -webkit-box-ordinal-group: 2; -webkit-order: 1; order: 1; } md-fab-speed-dial.md-down md-fab-actions { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; -webkit-box-ordinal-group: 3; -webkit-order: 2; order: 2; } md-fab-speed-dial.md-up { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; } md-fab-speed-dial.md-up md-fab-trigger { -webkit-box-ordinal-group: 3; -webkit-order: 2; order: 2; } md-fab-speed-dial.md-up md-fab-actions { -webkit-box-orient: vertical; -webkit-box-direction: reverse; -webkit-flex-direction: column-reverse; flex-direction: column-reverse; -webkit-box-ordinal-group: 2; -webkit-order: 1; order: 1; } md-fab-speed-dial.md-left { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-fab-speed-dial.md-left md-fab-trigger { -webkit-box-ordinal-group: 3; -webkit-order: 2; order: 2; } md-fab-speed-dial.md-left md-fab-actions { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -webkit-flex-direction: row-reverse; flex-direction: row-reverse; -webkit-box-ordinal-group: 2; -webkit-order: 1; order: 1; } md-fab-speed-dial.md-left md-fab-actions .md-fab-action-item { -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); } md-fab-speed-dial.md-right { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-fab-speed-dial.md-right md-fab-trigger { -webkit-box-ordinal-group: 2; -webkit-order: 1; order: 1; } md-fab-speed-dial.md-right md-fab-actions { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; -webkit-box-ordinal-group: 3; -webkit-order: 2; order: 2; } md-fab-speed-dial.md-right md-fab-actions .md-fab-action-item { -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); } md-fab-speed-dial.md-fling-remove .md-fab-action-item > *, md-fab-speed-dial.md-scale-remove .md-fab-action-item > * { visibility: hidden; } md-fab-speed-dial.md-fling .md-fab-action-item { opacity: 1; } md-fab-speed-dial.md-fling.md-animations-waiting .md-fab-action-item { opacity: 0; -webkit-transition-duration: 0s; transition-duration: 0s; } md-fab-speed-dial.md-scale .md-fab-action-item { -webkit-transform: scale(0); transform: scale(0); -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); -webkit-transition-duration: 0.14286s; transition-duration: 0.14286s; } md-fab-toolbar { display: block; /* * Closed styling */ /* * Hover styling */ } md-fab-toolbar.md-fab-bottom-right { top: auto; right: 20px; bottom: 20px; left: auto; position: absolute; } md-fab-toolbar.md-fab-bottom-left { top: auto; right: auto; bottom: 20px; left: 20px; position: absolute; } md-fab-toolbar.md-fab-top-right { top: 20px; right: 20px; bottom: auto; left: auto; position: absolute; } md-fab-toolbar.md-fab-top-left { top: 20px; right: auto; bottom: auto; left: 20px; position: absolute; } md-fab-toolbar .md-fab-toolbar-wrapper { display: block; position: relative; overflow: hidden; height: 68px; } md-fab-toolbar md-fab-trigger { position: absolute; z-index: 20; } md-fab-toolbar md-fab-trigger button { overflow: visible !important; } md-fab-toolbar md-fab-trigger .md-fab-toolbar-background { display: block; position: absolute; z-index: 21; opacity: 1; -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); } md-fab-toolbar md-fab-trigger md-icon { position: relative; z-index: 22; opacity: 1; -webkit-transition: all 200ms ease-in; transition: all 200ms ease-in; } md-fab-toolbar.md-left md-fab-trigger { right: 0; } [dir=rtl] md-fab-toolbar.md-left md-fab-trigger { right: auto; left: 0; } md-fab-toolbar.md-left .md-toolbar-tools { -webkit-box-orient: horizontal; -webkit-box-direction: reverse; -webkit-flex-direction: row-reverse; flex-direction: row-reverse; } md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { margin-right: 0.6rem; } [dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { margin-right: auto; margin-left: 0.6rem; } md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { margin-left: -0.8rem; } [dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools > .md-button:first-child { margin-left: auto; margin-right: -0.8rem; } md-fab-toolbar.md-left .md-toolbar-tools > .md-button:last-child { margin-right: 8px; } [dir=rtl] md-fab-toolbar.md-left .md-toolbar-tools > .md-button:last-child { margin-right: auto; margin-left: 8px; } md-fab-toolbar.md-right md-fab-trigger { left: 0; } [dir=rtl] md-fab-toolbar.md-right md-fab-trigger { left: auto; right: 0; } md-fab-toolbar.md-right .md-toolbar-tools { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; } md-fab-toolbar md-toolbar { background-color: transparent !important; pointer-events: none; z-index: 23; } md-fab-toolbar md-toolbar .md-toolbar-tools { padding: 0 20px; margin-top: 3px; } md-fab-toolbar md-toolbar .md-fab-action-item { opacity: 0; -webkit-transform: scale(0); transform: scale(0); -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); -webkit-transition-duration: 0.15s; transition-duration: 0.15s; } md-fab-toolbar.md-is-open md-fab-trigger > button { box-shadow: none; } md-fab-toolbar.md-is-open md-fab-trigger > button md-icon { opacity: 0; } md-fab-toolbar.md-is-open .md-fab-action-item { opacity: 1; -webkit-transform: scale(1); transform: scale(1); } md-icon { margin: auto; background-repeat: no-repeat no-repeat; display: inline-block; vertical-align: middle; fill: currentColor; height: 24px; width: 24px; min-height: 24px; min-width: 24px; } md-icon svg { pointer-events: none; display: block; } md-icon[md-font-icon] { line-height: 24px; width: auto; } md-grid-list { box-sizing: border-box; display: block; position: relative; } md-grid-list md-grid-tile, md-grid-list md-grid-tile > figure, md-grid-list md-grid-tile-header, md-grid-list md-grid-tile-footer { box-sizing: border-box; } md-grid-list md-grid-tile { display: block; position: absolute; } md-grid-list md-grid-tile figure { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; -webkit-box-pack: center; -webkit-justify-content: center; justify-content: center; height: 100%; position: absolute; top: 0; right: 0; bottom: 0; left: 0; padding: 0; margin: 0; } md-grid-list md-grid-tile md-grid-tile-header, md-grid-list md-grid-tile md-grid-tile-footer { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; -webkit-box-align: center; -webkit-align-items: center; align-items: center; height: 48px; color: #fff; background: rgba(0, 0, 0, 0.18); overflow: hidden; position: absolute; left: 0; right: 0; } md-grid-list md-grid-tile md-grid-tile-header h3, md-grid-list md-grid-tile md-grid-tile-header h4, md-grid-list md-grid-tile md-grid-tile-footer h3, md-grid-list md-grid-tile md-grid-tile-footer h4 { font-weight: 400; margin: 0 0 0 16px; } md-grid-list md-grid-tile md-grid-tile-header h3, md-grid-list md-grid-tile md-grid-tile-footer h3 { font-size: 14px; } md-grid-list md-grid-tile md-grid-tile-header h4, md-grid-list md-grid-tile md-grid-tile-footer h4 { font-size: 12px; } md-grid-list md-grid-tile md-grid-tile-header { top: 0; } md-grid-list md-grid-tile md-grid-tile-footer { bottom: 0; } @media screen and (-ms-high-contrast: active) { md-grid-tile { border: 1px solid #fff; } md-grid-tile-footer { border-top: 1px solid #fff; } } md-input-container { display: inline-block; position: relative; padding: 2px; margin: 18px 0; vertical-align: middle; /* * The .md-input class is added to the input/textarea */ } md-input-container:after { content: ''; display: table; clear: both; } md-input-container.md-block { display: block; } md-input-container .md-errors-spacer { float: right; min-height: 24px; min-width: 1px; } [dir=rtl] md-input-container .md-errors-spacer { float: left; } md-input-container > md-icon { position: absolute; top: 8px; left: 2px; right: auto; } [dir=rtl] md-input-container > md-icon { left: auto; } [dir=rtl] md-input-container > md-icon { right: 2px; } md-input-container textarea, md-input-container input[type="text"], md-input-container input[type="password"], md-input-container input[type="datetime"], md-input-container input[type="datetime-local"], md-input-container input[type="date"], md-input-container input[type="month"], md-input-container input[type="time"], md-input-container input[type="week"], md-input-container input[type="number"], md-input-container input[type="email"], md-input-container input[type="url"], md-input-container input[type="search"], md-input-container input[type="tel"], md-input-container input[type="color"] { /* remove default appearance from all input/textarea */ -moz-appearance: none; -webkit-appearance: none; } md-input-container input[type="date"], md-input-container input[type="datetime-local"], md-input-container input[type="month"], md-input-container input[type="time"], md-input-container input[type="week"] { min-height: 26px; } md-input-container textarea { resize: none; overflow: hidden; } md-input-container textarea.md-input { min-height: 26px; -ms-flex-preferred-size: auto; } md-input-container textarea[md-no-autogrow] { height: auto; overflow: auto; } md-input-container label:not(.md-container-ignore) { position: absolute; bottom: 100%; left: 0; right: auto; } [dir=rtl] md-input-container label:not(.md-container-ignore) { left: auto; } [dir=rtl] md-input-container label:not(.md-container-ignore) { right: 0; } md-input-container label:not(.md-container-ignore).md-required:after { content: ' *'; font-size: 13px; vertical-align: top; } md-input-container label:not(.md-no-float):not(.md-container-ignore), md-input-container .md-placeholder { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; width: 100%; -webkit-box-ordinal-group: 2; -webkit-order: 1; order: 1; pointer-events: none; -webkit-font-smoothing: antialiased; padding-left: 3px; padding-right: 0; z-index: 1; -webkit-transform: translate3d(0, 28px, 0) scale(1); transform: translate3d(0, 28px, 0) scale(1); -webkit-transition: -webkit-transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: -webkit-transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1), -webkit-transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); max-width: 100%; -webkit-transform-origin: left top; transform-origin: left top; } [dir=rtl] md-input-container label:not(.md-no-float):not(.md-container-ignore), [dir=rtl] md-input-container .md-placeholder { padding-left: 0; } [dir=rtl] md-input-container label:not(.md-no-float):not(.md-container-ignore), [dir=rtl] md-input-container .md-placeholder { padding-right: 3px; } [dir=rtl] md-input-container label:not(.md-no-float):not(.md-container-ignore), [dir=rtl] md-input-container .md-placeholder { -webkit-transform-origin: right top; transform-origin: right top; } md-input-container .md-placeholder { position: absolute; top: 0; opacity: 0; -webkit-transition-property: opacity, -webkit-transform; transition-property: opacity, -webkit-transform; transition-property: opacity, transform; transition-property: opacity, transform, -webkit-transform; -webkit-transform: translate3d(0, 30px, 0); transform: translate3d(0, 30px, 0); } md-input-container.md-input-focused .md-placeholder { opacity: 1; -webkit-transform: translate3d(0, 24px, 0); transform: translate3d(0, 24px, 0); } md-input-container.md-input-has-value .md-placeholder { -webkit-transition: none; transition: none; opacity: 0; } md-input-container:not(.md-input-has-value) input:not(:focus), md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-ampm-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-day-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-hour-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-millisecond-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-minute-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-month-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-second-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-week-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-year-field, md-input-container:not(.md-input-has-value) input:not(:focus)::-webkit-datetime-edit-text { color: transparent; } md-input-container .md-input { -webkit-box-ordinal-group: 3; -webkit-order: 2; order: 2; display: block; margin-top: 0; background: none; padding-top: 2px; padding-bottom: 1px; padding-left: 2px; padding-right: 2px; border-width: 0 0 1px 0; line-height: 26px; height: 30px; -ms-flex-preferred-size: 26px; border-radius: 0; border-style: solid; width: 100%; box-sizing: border-box; float: left; } [dir=rtl] md-input-container .md-input { float: right; } md-input-container .md-input:focus { outline: none; } md-input-container .md-input:invalid { outline: none; box-shadow: none; } md-input-container .md-input.md-no-flex { -webkit-box-flex: 0 !important; -webkit-flex: none !important; flex: none !important; } md-input-container .md-char-counter { text-align: right; padding-right: 2px; padding-left: 0; } [dir=rtl] md-input-container .md-char-counter { text-align: left; } [dir=rtl] md-input-container .md-char-counter { padding-right: 0; } [dir=rtl] md-input-container .md-char-counter { padding-left: 2px; } md-input-container .md-input-messages-animation { position: relative; -webkit-box-ordinal-group: 5; -webkit-order: 4; order: 4; overflow: hidden; clear: left; } [dir=rtl] md-input-container .md-input-messages-animation { clear: right; } md-input-container .md-input-message-animation, md-input-container .md-char-counter { font-size: 12px; line-height: 14px; overflow: hidden; -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); opacity: 1; margin-top: 0; padding-top: 5px; } md-input-container .md-input-message-animation:not(.md-char-counter), md-input-container .md-char-counter:not(.md-char-counter) { padding-right: 5px; padding-left: 0; } [dir=rtl] md-input-container .md-input-message-animation:not(.md-char-counter), [dir=rtl] md-input-container .md-char-counter:not(.md-char-counter) { padding-right: 0; } [dir=rtl] md-input-container .md-input-message-animation:not(.md-char-counter), [dir=rtl] md-input-container .md-char-counter:not(.md-char-counter) { padding-left: 5px; } md-input-container:not(.md-input-invalid) .md-auto-hide .md-input-message-animation { opacity: 0; margin-top: -100px; } md-input-container .md-input-message-animation.ng-enter-prepare { opacity: 0; margin-top: -100px; } md-input-container .md-input-message-animation.ng-enter:not(.ng-enter-active) { opacity: 0; margin-top: -100px; } md-input-container.md-input-focused label:not(.md-no-float), md-input-container.md-input-has-placeholder label:not(.md-no-float), md-input-container.md-input-has-value label:not(.md-no-float) { -webkit-transform: translate3d(0, 6px, 0) scale(0.75); transform: translate3d(0, 6px, 0) scale(0.75); -webkit-transition: width cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s, -webkit-transform cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s; transition: width cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s, -webkit-transform cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s; transition: transform cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s, width cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s; transition: transform cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s, width cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s, -webkit-transform cubic-bezier(0.25, 0.8, 0.25, 1) 0.4s; } md-input-container.md-input-has-value label { -webkit-transition: none; transition: none; } md-input-container.md-input-focused .md-input, md-input-container .md-input.ng-invalid.ng-dirty, md-input-container.md-input-resized .md-input { padding-bottom: 0; border-width: 0 0 2px 0; } md-input-container .md-input[disabled], [disabled] md-input-container .md-input { background-position: bottom -1px left 0; background-size: 4px 1px; background-repeat: repeat-x; } md-input-container.md-icon-float { -webkit-transition: margin-top 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: margin-top 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); } md-input-container.md-icon-float > label { pointer-events: none; position: absolute; } md-input-container.md-icon-float > md-icon { top: 8px; left: 2px; right: auto; } [dir=rtl] md-input-container.md-icon-float > md-icon { left: auto; } [dir=rtl] md-input-container.md-icon-float > md-icon { right: 2px; } md-input-container.md-icon-left > label:not(.md-no-float):not(.md-container-ignore), md-input-container.md-icon-left > label .md-placeholder, md-input-container.md-icon-right > label:not(.md-no-float):not(.md-container-ignore), md-input-container.md-icon-right > label .md-placeholder { width: calc(100% - 36px - 18px); } md-input-container.md-icon-left { padding-left: 36px; padding-right: 0; } [dir=rtl] md-input-container.md-icon-left { padding-left: 0; } [dir=rtl] md-input-container.md-icon-left { padding-right: 36px; } md-input-container.md-icon-left > label { left: 36px; right: auto; } [dir=rtl] md-input-container.md-icon-left > label { left: auto; } [dir=rtl] md-input-container.md-icon-left > label { right: 36px; } md-input-container.md-icon-right { padding-left: 0; padding-right: 36px; } [dir=rtl] md-input-container.md-icon-right { padding-left: 36px; } [dir=rtl] md-input-container.md-icon-right { padding-right: 0; } md-input-container.md-icon-right > md-icon:last-of-type { margin: 0; right: 2px; left: auto; } [dir=rtl] md-input-container.md-icon-right > md-icon:last-of-type { right: auto; } [dir=rtl] md-input-container.md-icon-right > md-icon:last-of-type { left: 2px; } md-input-container.md-icon-left.md-icon-right { padding-left: 36px; padding-right: 36px; } md-input-container.md-icon-left.md-icon-right > label:not(.md-no-float):not(.md-container-ignore), md-input-container.md-icon-left.md-icon-right > label .md-placeholder { width: calc(100% - (36px * 2)); } .md-resize-wrapper { position: relative; } .md-resize-wrapper:after { content: ''; display: table; clear: both; } .md-resize-handle { position: absolute; bottom: -5px; left: 0; height: 10px; background: transparent; width: 100%; cursor: ns-resize; } @media screen and (-ms-high-contrast: active) { md-input-container.md-default-theme > md-icon { fill: #fff; } } md-list { display: block; padding: 8px 0px 8px 0px; } md-list .md-subheader { font-size: 14px; font-weight: 500; letter-spacing: 0.010em; line-height: 1.2em; } md-list.md-dense md-list-item, md-list.md-dense md-list-item .md-list-item-inner { min-height: 48px; } md-list.md-dense md-list-item::before, md-list.md-dense md-list-item .md-list-item-inner::before { content: ''; min-height: 48px; visibility: hidden; display: inline-block; } md-list.md-dense md-list-item md-icon:first-child, md-list.md-dense md-list-item .md-list-item-inner md-icon:first-child { width: 20px; height: 20px; } md-list.md-dense md-list-item > md-icon:first-child:not(.md-avatar-icon), md-list.md-dense md-list-item .md-list-item-inner > md-icon:first-child:not(.md-avatar-icon) { margin-right: 36px; } [dir=rtl] md-list.md-dense md-list-item > md-icon:first-child:not(.md-avatar-icon), [dir=rtl] md-list.md-dense md-list-item .md-list-item-inner > md-icon:first-child:not(.md-avatar-icon) { margin-right: auto; margin-left: 36px; } md-list.md-dense md-list-item .md-avatar, md-list.md-dense md-list-item .md-avatar-icon, md-list.md-dense md-list-item .md-list-item-inner .md-avatar, md-list.md-dense md-list-item .md-list-item-inner .md-avatar-icon { margin-right: 20px; } [dir=rtl] md-list.md-dense md-list-item .md-avatar, [dir=rtl] md-list.md-dense md-list-item .md-avatar-icon, [dir=rtl] md-list.md-dense md-list-item .md-list-item-inner .md-avatar, [dir=rtl] md-list.md-dense md-list-item .md-list-item-inner .md-avatar-icon { margin-right: auto; margin-left: 20px; } md-list.md-dense md-list-item .md-avatar, md-list.md-dense md-list-item .md-list-item-inner .md-avatar { -webkit-box-flex: 0; -webkit-flex: none; flex: none; width: 36px; height: 36px; } md-list.md-dense md-list-item.md-2-line .md-list-item-text.md-offset, md-list.md-dense md-list-item.md-2-line > .md-no-style .md-list-item-text.md-offset, md-list.md-dense md-list-item.md-3-line .md-list-item-text.md-offset, md-list.md-dense md-list-item.md-3-line > .md-no-style .md-list-item-text.md-offset { margin-left: 56px; } [dir=rtl] md-list.md-dense md-list-item.md-2-line .md-list-item-text.md-offset, [dir=rtl] md-list.md-dense md-list-item.md-2-line > .md-no-style .md-list-item-text.md-offset, [dir=rtl] md-list.md-dense md-list-item.md-3-line .md-list-item-text.md-offset, [dir=rtl] md-list.md-dense md-list-item.md-3-line > .md-no-style .md-list-item-text.md-offset { margin-left: auto; margin-right: 56px; } md-list.md-dense md-list-item.md-2-line .md-list-item-text h3, md-list.md-dense md-list-item.md-2-line .md-list-item-text h4, md-list.md-dense md-list-item.md-2-line .md-list-item-text p, md-list.md-dense md-list-item.md-2-line > .md-no-style .md-list-item-text h3, md-list.md-dense md-list-item.md-2-line > .md-no-style .md-list-item-text h4, md-list.md-dense md-list-item.md-2-line > .md-no-style .md-list-item-text p, md-list.md-dense md-list-item.md-3-line .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line .md-list-item-text h4, md-list.md-dense md-list-item.md-3-line .md-list-item-text p, md-list.md-dense md-list-item.md-3-line > .md-no-style .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line > .md-no-style .md-list-item-text h4, md-list.md-dense md-list-item.md-3-line > .md-no-style .md-list-item-text p { line-height: 1.05; font-size: 12px; } md-list.md-dense md-list-item.md-2-line .md-list-item-text h3, md-list.md-dense md-list-item.md-2-line > .md-no-style .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line .md-list-item-text h3, md-list.md-dense md-list-item.md-3-line > .md-no-style .md-list-item-text h3 { font-size: 13px; } md-list.md-dense md-list-item.md-2-line, md-list.md-dense md-list-item.md-2-line > .md-no-style { min-height: 60px; } md-list.md-dense md-list-item.md-2-line::before, md-list.md-dense md-list-item.md-2-line > .md-no-style::before { content: ''; min-height: 60px; visibility: hidden; display: inline-block; } md-list.md-dense md-list-item.md-2-line > .md-avatar, md-list.md-dense md-list-item.md-2-line .md-avatar-icon, md-list.md-dense md-list-item.md-2-line > .md-no-style > .md-avatar, md-list.md-dense md-list-item.md-2-line > .md-no-style .md-avatar-icon { margin-top: 12px; } md-list.md-dense md-list-item.md-3-line, md-list.md-dense md-list-item.md-3-line > .md-no-style { min-height: 76px; } md-list.md-dense md-list-item.md-3-line::before, md-list.md-dense md-list-item.md-3-line > .md-no-style::before { content: ''; min-height: 76px; visibility: hidden; display: inline-block; } md-list.md-dense md-list-item.md-3-line > md-icon:first-child, md-list.md-dense md-list-item.md-3-line > .md-avatar, md-list.md-dense md-list-item.md-3-line > .md-no-style > md-icon:first-child, md-list.md-dense md-list-item.md-3-line > .md-no-style > .md-avatar { margin-top: 16px; } md-list-item { position: relative; } md-list-item.md-proxy-focus.md-focused .md-no-style { -webkit-transition: background-color 0.15s linear; transition: background-color 0.15s linear; } md-list-item._md-button-wrap { position: relative; } md-list-item._md-button-wrap > div.md-button:first-child { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; -webkit-box-pack: start; -webkit-justify-content: flex-start; justify-content: flex-start; padding: 0 16px; margin: 0; font-weight: 400; text-align: left; border: medium none; } [dir=rtl] md-list-item._md-button-wrap > div.md-button:first-child { text-align: right; } md-list-item._md-button-wrap > div.md-button:first-child > .md-button:first-child { position: absolute; top: 0; left: 0; height: 100%; margin: 0; padding: 0; } md-list-item._md-button-wrap > div.md-button:first-child .md-list-item-inner { width: 100%; min-height: inherit; } md-list-item.md-no-proxy, md-list-item .md-no-style { position: relative; padding: 0px 16px; -webkit-box-flex: 1; -webkit-flex: 1 1 auto; flex: 1 1 auto; } md-list-item.md-no-proxy.md-button, md-list-item .md-no-style.md-button { font-size: inherit; height: inherit; text-align: left; text-transform: none; width: 100%; white-space: normal; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: inherit; flex-direction: inherit; -webkit-box-align: inherit; -webkit-align-items: inherit; align-items: inherit; border-radius: 0; margin: 0; } [dir=rtl] md-list-item.md-no-proxy.md-button, [dir=rtl] md-list-item .md-no-style.md-button { text-align: right; } md-list-item.md-no-proxy.md-button > .md-ripple-container, md-list-item .md-no-style.md-button > .md-ripple-container { border-radius: 0; } md-list-item.md-no-proxy:focus, md-list-item .md-no-style:focus { outline: none; } md-list-item.md-clickable:hover { cursor: pointer; } md-list-item md-divider { position: absolute; bottom: 0; left: 0; width: 100%; } [dir=rtl] md-list-item md-divider { left: auto; right: 0; } md-list-item md-divider[md-inset] { left: 72px; width: calc(100% - 72px); margin: 0 !important; } [dir=rtl] md-list-item md-divider[md-inset] { left: auto; right: 72px; } md-list-item, md-list-item .md-list-item-inner { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-pack: start; -webkit-justify-content: flex-start; justify-content: flex-start; -webkit-box-align: center; -webkit-align-items: center; align-items: center; min-height: 48px; height: auto; } md-list-item::before, md-list-item .md-list-item-inner::before { content: ''; min-height: 48px; visibility: hidden; display: inline-block; } md-list-item > div.md-primary > md-icon:not(.md-avatar-icon), md-list-item > div.md-secondary > md-icon:not(.md-avatar-icon), md-list-item > md-icon:first-child:not(.md-avatar-icon), md-list-item > md-icon.md-secondary:not(.md-avatar-icon), md-list-item .md-list-item-inner > div.md-primary > md-icon:not(.md-avatar-icon), md-list-item .md-list-item-inner > div.md-secondary > md-icon:not(.md-avatar-icon), md-list-item .md-list-item-inner > md-icon:first-child:not(.md-avatar-icon), md-list-item .md-list-item-inner > md-icon.md-secondary:not(.md-avatar-icon) { width: 24px; margin-top: 16px; margin-bottom: 12px; box-sizing: content-box; } md-list-item > div.md-primary > md-checkbox, md-list-item > div.md-secondary > md-checkbox, md-list-item > md-checkbox, md-list-item md-checkbox.md-secondary, md-list-item .md-list-item-inner > div.md-primary > md-checkbox, md-list-item .md-list-item-inner > div.md-secondary > md-checkbox, md-list-item .md-list-item-inner > md-checkbox, md-list-item .md-list-item-inner md-checkbox.md-secondary { -webkit-align-self: center; -ms-grid-row-align: center; align-self: center; } md-list-item > div.md-primary > md-checkbox .md-label, md-list-item > div.md-secondary > md-checkbox .md-label, md-list-item > md-checkbox .md-label, md-list-item md-checkbox.md-secondary .md-label, md-list-item .md-list-item-inner > div.md-primary > md-checkbox .md-label, md-list-item .md-list-item-inner > div.md-secondary > md-checkbox .md-label, md-list-item .md-list-item-inner > md-checkbox .md-label, md-list-item .md-list-item-inner md-checkbox.md-secondary .md-label { display: none; } md-list-item > md-icon:first-child:not(.md-avatar-icon), md-list-item .md-list-item-inner > md-icon:first-child:not(.md-avatar-icon) { margin-right: 32px; } [dir=rtl] md-list-item > md-icon:first-child:not(.md-avatar-icon), [dir=rtl] md-list-item .md-list-item-inner > md-icon:first-child:not(.md-avatar-icon) { margin-right: auto; margin-left: 32px; } md-list-item .md-avatar, md-list-item .md-avatar-icon, md-list-item .md-list-item-inner .md-avatar, md-list-item .md-list-item-inner .md-avatar-icon { margin-top: 8px; margin-bottom: 8px; margin-right: 16px; border-radius: 50%; box-sizing: content-box; } [dir=rtl] md-list-item .md-avatar, [dir=rtl] md-list-item .md-avatar-icon, [dir=rtl] md-list-item .md-list-item-inner .md-avatar, [dir=rtl] md-list-item .md-list-item-inner .md-avatar-icon { margin-right: auto; margin-left: 16px; } md-list-item .md-avatar, md-list-item .md-list-item-inner .md-avatar { -webkit-box-flex: 0; -webkit-flex: none; flex: none; width: 40px; height: 40px; } md-list-item .md-avatar-icon, md-list-item .md-list-item-inner .md-avatar-icon { padding: 8px; } md-list-item .md-avatar-icon svg, md-list-item .md-list-item-inner .md-avatar-icon svg { width: 24px; height: 24px; } md-list-item > md-checkbox, md-list-item .md-list-item-inner > md-checkbox { width: 24px; margin-left: 3px; margin-right: 29px; margin-top: 16px; } [dir=rtl] md-list-item > md-checkbox, [dir=rtl] md-list-item .md-list-item-inner > md-checkbox { margin-left: 29px; } [dir=rtl] md-list-item > md-checkbox, [dir=rtl] md-list-item .md-list-item-inner > md-checkbox { margin-right: 3px; } md-list-item .md-secondary-container, md-list-item .md-list-item-inner .md-secondary-container { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-align: center; -webkit-align-items: center; align-items: center; -webkit-flex-shrink: 0; flex-shrink: 0; margin: auto; margin-right: 0; margin-left: auto; } [dir=rtl] md-list-item .md-secondary-container, [dir=rtl] md-list-item .md-list-item-inner .md-secondary-container { margin-right: auto; } [dir=rtl] md-list-item .md-secondary-container, [dir=rtl] md-list-item .md-list-item-inner .md-secondary-container { margin-left: 0; } md-list-item .md-secondary-container .md-button:last-of-type, md-list-item .md-secondary-container .md-icon-button:last-of-type, md-list-item .md-list-item-inner .md-secondary-container .md-button:last-of-type, md-list-item .md-list-item-inner .md-secondary-container .md-icon-button:last-of-type { margin-right: 0; } [dir=rtl] md-list-item .md-secondary-container .md-button:last-of-type, [dir=rtl] md-list-item .md-secondary-container .md-icon-button:last-of-type, [dir=rtl] md-list-item .md-list-item-inner .md-secondary-container .md-button:last-of-type, [dir=rtl] md-list-item .md-list-item-inner .md-secondary-container .md-icon-button:last-of-type { margin-right: auto; margin-left: 0; } md-list-item .md-secondary-container md-checkbox, md-list-item .md-list-item-inner .md-secondary-container md-checkbox { margin-top: 0; margin-bottom: 0; } md-list-item .md-secondary-container md-checkbox:last-child, md-list-item .md-list-item-inner .md-secondary-container md-checkbox:last-child { width: 24px; margin-right: 0; } [dir=rtl] md-list-item .md-secondary-container md-checkbox:last-child, [dir=rtl] md-list-item .md-list-item-inner .md-secondary-container md-checkbox:last-child { margin-right: auto; margin-left: 0; } md-list-item .md-secondary-container md-switch, md-list-item .md-list-item-inner .md-secondary-container md-switch { margin-top: 0; margin-bottom: 0; margin-right: -6px; } [dir=rtl] md-list-item .md-secondary-container md-switch, [dir=rtl] md-list-item .md-list-item-inner .md-secondary-container md-switch { margin-right: auto; margin-left: -6px; } md-list-item > p, md-list-item > .md-list-item-inner > p, md-list-item .md-list-item-inner > p, md-list-item .md-list-item-inner > .md-list-item-inner > p { -webkit-box-flex: 1; -webkit-flex: 1 1 auto; flex: 1 1 auto; margin: 0; } md-list-item.md-2-line, md-list-item.md-2-line > .md-no-style, md-list-item.md-3-line, md-list-item.md-3-line > .md-no-style { -webkit-box-align: start; -webkit-align-items: flex-start; align-items: flex-start; -webkit-box-pack: center; -webkit-justify-content: center; justify-content: center; } md-list-item.md-2-line.md-long-text, md-list-item.md-2-line > .md-no-style.md-long-text, md-list-item.md-3-line.md-long-text, md-list-item.md-3-line > .md-no-style.md-long-text { margin-top: 8px; margin-bottom: 8px; } md-list-item.md-2-line .md-list-item-text, md-list-item.md-2-line > .md-no-style .md-list-item-text, md-list-item.md-3-line .md-list-item-text, md-list-item.md-3-line > .md-no-style .md-list-item-text { -webkit-box-flex: 1; -webkit-flex: 1 1 auto; flex: 1 1 auto; margin: auto; text-overflow: ellipsis; overflow: hidden; } md-list-item.md-2-line .md-list-item-text.md-offset, md-list-item.md-2-line > .md-no-style .md-list-item-text.md-offset, md-list-item.md-3-line .md-list-item-text.md-offset, md-list-item.md-3-line > .md-no-style .md-list-item-text.md-offset { margin-left: 56px; } [dir=rtl] md-list-item.md-2-line .md-list-item-text.md-offset, [dir=rtl] md-list-item.md-2-line > .md-no-style .md-list-item-text.md-offset, [dir=rtl] md-list-item.md-3-line .md-list-item-text.md-offset, [dir=rtl] md-list-item.md-3-line > .md-no-style .md-list-item-text.md-offset { margin-left: auto; margin-right: 56px; } md-list-item.md-2-line .md-list-item-text h3, md-list-item.md-2-line > .md-no-style .md-list-item-text h3, md-list-item.md-3-line .md-list-item-text h3, md-list-item.md-3-line > .md-no-style .md-list-item-text h3 { font-size: 16px; font-weight: 400; letter-spacing: 0.010em; margin: 0 0 0px 0; line-height: 1.2em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } md-list-item.md-2-line .md-list-item-text h4, md-list-item.md-2-line > .md-no-style .md-list-item-text h4, md-list-item.md-3-line .md-list-item-text h4, md-list-item.md-3-line > .md-no-style .md-list-item-text h4 { font-size: 14px; letter-spacing: 0.010em; margin: 3px 0 1px 0; font-weight: 400; line-height: 1.2em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } md-list-item.md-2-line .md-list-item-text p, md-list-item.md-2-line > .md-no-style .md-list-item-text p, md-list-item.md-3-line .md-list-item-text p, md-list-item.md-3-line > .md-no-style .md-list-item-text p { font-size: 14px; font-weight: 500; letter-spacing: 0.010em; margin: 0 0 0 0; line-height: 1.6em; } md-list-item.md-2-line, md-list-item.md-2-line > .md-no-style { height: auto; min-height: 72px; } md-list-item.md-2-line::before, md-list-item.md-2-line > .md-no-style::before { content: ''; min-height: 72px; visibility: hidden; display: inline-block; } md-list-item.md-2-line > .md-avatar, md-list-item.md-2-line .md-avatar-icon, md-list-item.md-2-line > .md-no-style > .md-avatar, md-list-item.md-2-line > .md-no-style .md-avatar-icon { margin-top: 12px; } md-list-item.md-2-line > md-icon:first-child, md-list-item.md-2-line > .md-no-style > md-icon:first-child { -webkit-align-self: flex-start; align-self: flex-start; } md-list-item.md-2-line .md-list-item-text, md-list-item.md-2-line > .md-no-style .md-list-item-text { -webkit-box-flex: 1; -webkit-flex: 1 1 auto; flex: 1 1 auto; } md-list-item.md-3-line, md-list-item.md-3-line > .md-no-style { height: auto; min-height: 88px; } md-list-item.md-3-line::before, md-list-item.md-3-line > .md-no-style::before { content: ''; min-height: 88px; visibility: hidden; display: inline-block; } md-list-item.md-3-line > md-icon:first-child, md-list-item.md-3-line > .md-avatar, md-list-item.md-3-line > .md-no-style > md-icon:first-child, md-list-item.md-3-line > .md-no-style > .md-avatar { margin-top: 16px; } .md-open-menu-container { position: fixed; left: 0; top: 0; z-index: 100; opacity: 0; border-radius: 2px; max-height: calc(100vh - 10px); overflow: auto; } .md-open-menu-container md-menu-divider { margin-top: 4px; margin-bottom: 4px; height: 1px; min-height: 1px; max-height: 1px; width: 100%; } .md-open-menu-container md-menu-content > * { opacity: 0; } .md-open-menu-container:not(.md-clickable) { pointer-events: none; } .md-open-menu-container.md-active { opacity: 1; -webkit-transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); -webkit-transition-duration: 200ms; transition-duration: 200ms; } .md-open-menu-container.md-active > md-menu-content > * { opacity: 1; -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); -webkit-transition-duration: 200ms; transition-duration: 200ms; -webkit-transition-delay: 100ms; transition-delay: 100ms; } .md-open-menu-container.md-leave { opacity: 0; -webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); transition: all 0.3s cubic-bezier(0.55, 0, 0.55, 0.2); -webkit-transition-duration: 250ms; transition-duration: 250ms; } md-menu-content { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; flex-direction: column; padding: 8px 0; max-height: 304px; overflow-y: auto; } md-menu-content.md-dense { max-height: 208px; } md-menu-content.md-dense md-menu-item { height: 32px; min-height: 0px; } md-menu-item { display: -webkit-box; display: -webkit-flex; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; min-height: 48px; height: 48px; -webkit-align-content: center; align-content: center; -webkit-box-pack: start; -webkit-justify-content: flex-start; justify-content: flex-start; /* * We cannot use flex on '; } } function postLink(scope, element, attr) { $mdTheming(element); $mdButtonInkRipple.attach(scope, element); // Use async expect to support possible bindings in the button label $mdAria.expectWithoutText(element, 'aria-label'); // For anchor elements, we have to set tabindex manually when the // element is disabled if (isAnchor(attr) && angular.isDefined(attr.ngDisabled) ) { scope.$watch(attr.ngDisabled, function(isDisabled) { element.attr('tabindex', isDisabled ? -1 : 0); }); } // disabling click event when disabled is true element.on('click', function(e){ if (attr.disabled === true) { e.preventDefault(); e.stopImmediatePropagation(); } }); if (!element.hasClass('md-no-focus')) { element.on('focus', function() { // Only show the focus effect when being focused through keyboard interaction or programmatically if (!$mdInteraction.isUserInvoked() || $mdInteraction.getLastInteractionType() === 'keyboard') { element.addClass('md-focused'); } }); element.on('blur', function() { element.removeClass('md-focused'); }); } } } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.card * * @description * Card components. */ mdCardDirective.$inject = ["$mdTheming"]; angular.module('material.components.card', [ 'material.core' ]) .directive('mdCard', mdCardDirective); /** * @ngdoc directive * @name mdCard * @module material.components.card * * @restrict E * * @description * The `` directive is a container element used within `` containers. * * An image included as a direct descendant will fill the card's width. If you want to avoid this, * you can add the `md-image-no-fill` class to the parent element. The `` * container will wrap text content and provide padding. An `` element can be * optionally included to put content flush against the bottom edge of the card. * * Action buttons can be included in an `` element, similar to ``. * You can then position buttons using layout attributes. * * Card is built with: * * `` - Header for the card, holds avatar, text and squared image * - `` - Card avatar * - `md-user-avatar` - Class for user image * - `` * - `` - Contains elements for the card description * - `md-title` - Class for the card title * - `md-subhead` - Class for the card sub header * * `` - Image for the card * * `` - Card content title * - `` * - `md-headline` - Class for the card content title * - `md-subhead` - Class for the card content sub header * - `` - Squared image within the title * - `md-media-sm` - Class for small image * - `md-media-md` - Class for medium image * - `md-media-lg` - Class for large image * - `md-media-xl` - Class for extra large image * * `` - Card content * * `` - Card actions * - `` - Icon actions * * Cards have constant width and variable heights; where the maximum height is limited to what can * fit within a single view on a platform, but it can temporarily expand as needed. * * @usage * ### Card with optional footer * * * image caption * *

Card headline

*

Card content

*
* * Card footer * *
*
* * ### Card with actions * * * image caption * *

Card headline

*

Card content

*
* * Action 1 * Action 2 * *
*
* * ### Card with header, image, title actions and content * * * * * * * * Title * Sub header * * * image caption * * * Card headline * Card subheader * * * * Action 1 * Action 2 * * * * * * * *

* Card content *

*
*
*
*/ function mdCardDirective($mdTheming) { return { restrict: 'E', link: function ($scope, $element, attr) { $element.addClass('_md'); // private md component indicator for styling $mdTheming($element); } }; } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.chips */ /* * @see js folder for chips implementation */ angular.module('material.components.chips', [ 'material.core', 'material.components.autocomplete' ]); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.checkbox * @description Checkbox module! */ MdCheckboxDirective.$inject = ["inputDirective", "$mdAria", "$mdConstant", "$mdTheming", "$mdUtil", "$mdInteraction"]; angular .module('material.components.checkbox', ['material.core']) .directive('mdCheckbox', MdCheckboxDirective); /** * @ngdoc directive * @name mdCheckbox * @module material.components.checkbox * @restrict E * * @description * The checkbox directive is used like the normal [angular checkbox](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D). * * As per the [material design spec](http://www.google.com/design/spec/style/color.html#color-color-schemes) * the checkbox is in the accent color by default. The primary color palette may be used with * the `md-primary` class. * * @param {string} ng-model Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {expression=} ng-true-value The value to which the expression should be set when selected. * @param {expression=} ng-false-value The value to which the expression should be set when not selected. * @param {string=} ng-change AngularJS expression to be executed when input changes due to user interaction with the input element. * @param {boolean=} md-no-ink Use of attribute indicates use of ripple ink effects * @param {string=} aria-label Adds label to checkbox for accessibility. * Defaults to checkbox's text. If no default text is found, a warning will be logged. * @param {expression=} md-indeterminate This determines when the checkbox should be rendered as 'indeterminate'. * If a truthy expression or no value is passed in the checkbox renders in the md-indeterminate state. * If falsy expression is passed in it just looks like a normal unchecked checkbox. * The indeterminate, checked, and unchecked states are mutually exclusive. A box cannot be in any two states at the same time. * Adding the 'md-indeterminate' attribute overrides any checked/unchecked rendering logic. * When using the 'md-indeterminate' attribute use 'ng-checked' to define rendering logic instead of using 'ng-model'. * @param {expression=} ng-checked If this expression evaluates as truthy, the 'md-checked' css class is added to the checkbox and it * will appear checked. * * @usage * * * Finished ? * * * * No Ink Effects * * * * Disabled * * * * */ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $mdUtil, $mdInteraction) { inputDirective = inputDirective[0]; return { restrict: 'E', transclude: true, require: ['^?mdInputContainer', '?ngModel', '?^form'], priority: $mdConstant.BEFORE_NG_ARIA, template: '
' + '
' + '
' + '
', compile: compile }; // ********************************************************** // Private Methods // ********************************************************** function compile (tElement, tAttrs) { tAttrs.$set('tabindex', tAttrs.tabindex || '0'); tAttrs.$set('type', 'checkbox'); tAttrs.$set('role', tAttrs.type); return { pre: function(scope, element) { // Attach a click handler during preLink, in order to immediately stop propagation // (especially for ng-click) when the checkbox is disabled. element.on('click', function(e) { if (this.hasAttribute('disabled')) { e.stopImmediatePropagation(); } }); }, post: postLink }; function postLink(scope, element, attr, ctrls) { var isIndeterminate; var containerCtrl = ctrls[0]; var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); var formCtrl = ctrls[2]; if (containerCtrl) { var isErrorGetter = containerCtrl.isErrorGetter || function() { return ngModelCtrl.$invalid && (ngModelCtrl.$touched || (formCtrl && formCtrl.$submitted)); }; containerCtrl.input = element; scope.$watch(isErrorGetter, containerCtrl.setInvalid); } $mdTheming(element); // Redirect focus events to the root element, because IE11 is always focusing the container element instead // of the md-checkbox element. This causes issues when using ngModelOptions: `updateOnBlur` element.children().on('focus', function() { element.focus(); }); if ($mdUtil.parseAttributeBoolean(attr.mdIndeterminate)) { setIndeterminateState(); scope.$watch(attr.mdIndeterminate, setIndeterminateState); } if (attr.ngChecked) { scope.$watch(scope.$eval.bind(scope, attr.ngChecked), function(value) { ngModelCtrl.$setViewValue(value); ngModelCtrl.$render(); }); } $$watchExpr('ngDisabled', 'tabindex', { true: '-1', false: attr.tabindex }); $mdAria.expectWithText(element, 'aria-label'); // Reuse the original input[type=checkbox] directive from AngularJS core. // This is a bit hacky as we need our own event listener and own render // function. inputDirective.link.pre(scope, { on: angular.noop, 0: {} }, attr, [ngModelCtrl]); element.on('click', listener) .on('keypress', keypressHandler) .on('focus', function() { if ($mdInteraction.getLastInteractionType() === 'keyboard') { element.addClass('md-focused'); } }) .on('blur', function() { element.removeClass('md-focused'); }); ngModelCtrl.$render = render; function $$watchExpr(expr, htmlAttr, valueOpts) { if (attr[expr]) { scope.$watch(attr[expr], function(val) { if (valueOpts[val]) { element.attr(htmlAttr, valueOpts[val]); } }); } } function keypressHandler(ev) { var keyCode = ev.which || ev.keyCode; if (keyCode === $mdConstant.KEY_CODE.SPACE || keyCode === $mdConstant.KEY_CODE.ENTER) { ev.preventDefault(); element.addClass('md-focused'); listener(ev); } } function listener(ev) { // skipToggle boolean is used by the switch directive to prevent the click event // when releasing the drag. There will be always a click if releasing the drag over the checkbox if (element[0].hasAttribute('disabled') || scope.skipToggle) { return; } scope.$apply(function() { // Toggle the checkbox value... var viewValue = attr.ngChecked && attr.ngClick ? attr.checked : !ngModelCtrl.$viewValue; ngModelCtrl.$setViewValue(viewValue, ev && ev.type); ngModelCtrl.$render(); }); } function render() { // Cast the $viewValue to a boolean since it could be undefined element.toggleClass('md-checked', !!ngModelCtrl.$viewValue && !isIndeterminate); } function setIndeterminateState(newValue) { isIndeterminate = newValue !== false; if (isIndeterminate) { element.attr('aria-checked', 'mixed'); } element.toggleClass('md-indeterminate', isIndeterminate); } } } } })(); (function(){ "use strict"; (function () { "use strict"; /** * Use a RegExp to check if the `md-colors=""` is static string * or one that should be observed and dynamically interpolated. */ MdColorsDirective.$inject = ["$mdColors", "$mdUtil", "$log", "$parse"]; MdColorsService.$inject = ["$mdTheming", "$mdUtil", "$log"]; var STATIC_COLOR_EXPRESSION = /^{((\s|,)*?["'a-zA-Z-]+?\s*?:\s*?('|")[a-zA-Z0-9-.]*('|"))+\s*}$/; var colorPalettes = null; /** * @ngdoc module * @name material.components.colors * * @description * Define $mdColors service and a `md-colors=""` attribute directive */ angular .module('material.components.colors', ['material.core']) .directive('mdColors', MdColorsDirective) .service('$mdColors', MdColorsService); /** * @ngdoc service * @name $mdColors * @module material.components.colors * * @description * With only defining themes, one couldn't get non AngularJS Material elements colored with Material colors, * `$mdColors` service is used by the md-color directive to convert the 1..n color expressions to RGBA values and will apply * those values to element as CSS property values. * * @usage * * angular.controller('myCtrl', function ($mdColors) { * var color = $mdColors.getThemeColor('myTheme-red-200-0.5'); * ... * }); * * */ function MdColorsService($mdTheming, $mdUtil, $log) { colorPalettes = colorPalettes || Object.keys($mdTheming.PALETTES); // Publish service instance return { applyThemeColors: applyThemeColors, getThemeColor: getThemeColor, hasTheme: hasTheme }; // ******************************************** // Internal Methods // ******************************************** /** * @ngdoc method * @name $mdColors#applyThemeColors * * @description * Gets a color json object, keys are css properties and values are string of the wanted color * Then calculate the rgba() values based on the theme color parts * * @param {DOMElement} element the element to apply the styles on. * @param {object} colorExpression json object, keys are css properties and values are string of the wanted color, * for example: `{color: 'red-A200-0.3'}`. * * @usage * * app.directive('myDirective', function($mdColors) { * return { * ... * link: function (scope, elem) { * $mdColors.applyThemeColors(elem, {color: 'red'}); * } * } * }); * */ function applyThemeColors(element, colorExpression) { try { if (colorExpression) { // Assign the calculate RGBA color values directly as inline CSS element.css(interpolateColors(colorExpression)); } } catch (e) { $log.error(e.message); } } /** * @ngdoc method * @name $mdColors#getThemeColor * * @description * Get parsed color from expression * * @param {string} expression string of a color expression (for instance `'red-700-0.8'`) * * @returns {string} a css color expression (for instance `rgba(211, 47, 47, 0.8)`) * * @usage * * angular.controller('myCtrl', function ($mdColors) { * var color = $mdColors.getThemeColor('myTheme-red-200-0.5'); * ... * }); * */ function getThemeColor(expression) { var color = extractColorOptions(expression); return parseColor(color); } /** * Return the parsed color * @param color hashmap of color definitions * @param contrast whether use contrast color for foreground * @returns rgba color string */ function parseColor(color, contrast) { contrast = contrast || false; var rgbValues = $mdTheming.PALETTES[color.palette][color.hue]; rgbValues = contrast ? rgbValues.contrast : rgbValues.value; return $mdUtil.supplant('rgba({0}, {1}, {2}, {3})', [rgbValues[0], rgbValues[1], rgbValues[2], rgbValues[3] || color.opacity] ); } /** * Convert the color expression into an object with scope-interpolated values * Then calculate the rgba() values based on the theme color parts * * @results Hashmap of CSS properties with associated `rgba( )` string vales * * */ function interpolateColors(themeColors) { var rgbColors = {}; var hasColorProperty = themeColors.hasOwnProperty('color'); angular.forEach(themeColors, function (value, key) { var color = extractColorOptions(value); var hasBackground = key.indexOf('background') > -1; rgbColors[key] = parseColor(color); if (hasBackground && !hasColorProperty) { rgbColors.color = parseColor(color, true); } }); return rgbColors; } /** * Check if expression has defined theme * e.g. * 'myTheme-primary' => true * 'red-800' => false */ function hasTheme(expression) { return angular.isDefined($mdTheming.THEMES[expression.split('-')[0]]); } /** * For the evaluated expression, extract the color parts into a hash map */ function extractColorOptions(expression) { var parts = expression.split('-'); var hasTheme = angular.isDefined($mdTheming.THEMES[parts[0]]); var theme = hasTheme ? parts.splice(0, 1)[0] : $mdTheming.defaultTheme(); return { theme: theme, palette: extractPalette(parts, theme), hue: extractHue(parts, theme), opacity: parts[2] || 1 }; } /** * Calculate the theme palette name */ function extractPalette(parts, theme) { // If the next section is one of the palettes we assume it's a two word palette // Two word palette can be also written in camelCase, forming camelCase to dash-case var isTwoWord = parts.length > 1 && colorPalettes.indexOf(parts[1]) !== -1; var palette = parts[0].replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); if (isTwoWord) palette = parts[0] + '-' + parts.splice(1, 1); if (colorPalettes.indexOf(palette) === -1) { // If the palette is not in the palette list it's one of primary/accent/warn/background var scheme = $mdTheming.THEMES[theme].colors[palette]; if (!scheme) { throw new Error($mdUtil.supplant('mdColors: couldn\'t find \'{palette}\' in the palettes.', {palette: palette})); } palette = scheme.name; } return palette; } function extractHue(parts, theme) { var themeColors = $mdTheming.THEMES[theme].colors; if (parts[1] === 'hue') { var hueNumber = parseInt(parts.splice(2, 1)[0], 10); if (hueNumber < 1 || hueNumber > 3) { throw new Error($mdUtil.supplant('mdColors: \'hue-{hueNumber}\' is not a valid hue, can be only \'hue-1\', \'hue-2\' and \'hue-3\'', {hueNumber: hueNumber})); } parts[1] = 'hue-' + hueNumber; if (!(parts[0] in themeColors)) { throw new Error($mdUtil.supplant('mdColors: \'hue-x\' can only be used with [{availableThemes}], but was used with \'{usedTheme}\'', { availableThemes: Object.keys(themeColors).join(', '), usedTheme: parts[0] })); } return themeColors[parts[0]].hues[parts[1]]; } return parts[1] || themeColors[parts[0] in themeColors ? parts[0] : 'primary'].hues['default']; } } /** * @ngdoc directive * @name mdColors * @module material.components.colors * * @restrict A * * @description * `mdColors` directive will apply the theme-based color expression as RGBA CSS style values. * * The format will be similar to our color defining in the scss files: * * ## `[?theme]-[palette]-[?hue]-[?opacity]` * - [theme] - default value is the default theme * - [palette] - can be either palette name or primary/accent/warn/background * - [hue] - default is 500 (hue-x can be used with primary/accent/warn/background) * - [opacity] - default is 1 * * > `?` indicates optional parameter * * @usage * *
*
* Color demo *
*
*
* * `mdColors` directive will automatically watch for changes in the expression if it recognizes an interpolation * expression or a function. For performance options, you can use `::` prefix to the `md-colors` expression * to indicate a one-time data binding. * * * * * */ function MdColorsDirective($mdColors, $mdUtil, $log, $parse) { return { restrict: 'A', require: ['^?mdTheme'], compile: function (tElem, tAttrs) { var shouldWatch = shouldColorsWatch(); return function (scope, element, attrs, ctrl) { var mdThemeController = ctrl[0]; var lastColors = {}; var parseColors = function (theme) { if (typeof theme !== 'string') { theme = ''; } if (!attrs.mdColors) { attrs.mdColors = '{}'; } /** * Json.parse() does not work because the keys are not quoted; * use $parse to convert to a hash map */ var colors = $parse(attrs.mdColors)(scope); /** * If mdTheme is defined up the DOM tree * we add mdTheme theme to colors who doesn't specified a theme * * # example * *
*
* Color demo *
*
*
* * 'primary-600' will be 'myTheme-primary-600', * but 'mySecondTheme-accent-200' will stay the same cause it has a theme prefix */ if (mdThemeController) { Object.keys(colors).forEach(function (prop) { var color = colors[prop]; if (!$mdColors.hasTheme(color)) { colors[prop] = (theme || mdThemeController.$mdTheme) + '-' + color; } }); } cleanElement(colors); return colors; }; var cleanElement = function (colors) { if (!angular.equals(colors, lastColors)) { var keys = Object.keys(lastColors); if (lastColors.background && !keys.color) { keys.push('color'); } keys.forEach(function (key) { element.css(key, ''); }); } lastColors = colors; }; /** * Registering for mgTheme changes and asking mdTheme controller run our callback whenever a theme changes */ var unregisterChanges = angular.noop; if (mdThemeController) { unregisterChanges = mdThemeController.registerChanges(function (theme) { $mdColors.applyThemeColors(element, parseColors(theme)); }); } scope.$on('$destroy', function () { unregisterChanges(); }); try { if (shouldWatch) { scope.$watch(parseColors, angular.bind(this, $mdColors.applyThemeColors, element ), true); } else { $mdColors.applyThemeColors(element, parseColors()); } } catch (e) { $log.error(e.message); } }; function shouldColorsWatch() { // Simulate 1x binding and mark mdColorsWatch == false var rawColorExpression = tAttrs.mdColors; var bindOnce = rawColorExpression.indexOf('::') > -1; var isStatic = bindOnce ? true : STATIC_COLOR_EXPRESSION.test(tAttrs.mdColors); // Remove it for the postLink... tAttrs.mdColors = rawColorExpression.replace('::', ''); var hasWatchAttr = angular.isDefined(tAttrs.mdColorsWatch); return (bindOnce || isStatic) ? false : hasWatchAttr ? $mdUtil.parseAttributeBoolean(tAttrs.mdColorsWatch) : true; } } }; } })(); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.content * * @description * Scrollable content */ mdContentDirective.$inject = ["$mdTheming"]; angular.module('material.components.content', [ 'material.core' ]) .directive('mdContent', mdContentDirective); /** * @ngdoc directive * @name mdContent * @module material.components.content * * @restrict E * * @description * * The `` directive is a container element useful for scrollable content. It achieves * this by setting the CSS `overflow` property to `auto` so that content can properly scroll. * * In general, `` components are not designed to be nested inside one another. If * possible, it is better to make them siblings. This often results in a better user experience as * having nested scrollbars may confuse the user. * * ## Troubleshooting * * In some cases, you may wish to apply the `md-no-momentum` class to ensure that Safari's * momentum scrolling is disabled. Momentum scrolling can cause flickering issues while scrolling * SVG icons and some other components. * * Additionally, we now also offer the `md-no-flicker` class which can be applied to any element * and uses a Webkit-specific filter of `blur(0px)` that forces GPU rendering of all elements * inside (which eliminates the flicker on iOS devices). * * _Note: Forcing an element to render on the GPU can have unintended side-effects, especially * related to the z-index of elements. Please use with caution and only on the elements needed._ * * @usage * * Add the `[layout-padding]` attribute to make the content padded. * * * * Lorem ipsum dolor sit amet, ne quod novum mei. * * */ function mdContentDirective($mdTheming) { return { restrict: 'E', controller: ['$scope', '$element', ContentController], link: function(scope, element) { element.addClass('_md'); // private md component indicator for styling $mdTheming(element); scope.$broadcast('$mdContentLoaded', element); iosScrollFix(element[0]); } }; function ContentController($scope, $element) { this.$scope = $scope; this.$element = $element; } } function iosScrollFix(node) { // IOS FIX: // If we scroll where there is no more room for the webview to scroll, // by default the webview itself will scroll up and down, this looks really // bad. So if we are scrolling to the very top or bottom, add/subtract one angular.element(node).on('$md.pressdown', function(ev) { // Only touch events if (ev.pointer.type !== 't') return; // Don't let a child content's touchstart ruin it for us. if (ev.$materialScrollFixed) return; ev.$materialScrollFixed = true; if (node.scrollTop === 0) { node.scrollTop = 1; } else if (node.scrollHeight === node.scrollTop + node.offsetHeight) { node.scrollTop -= 1; } }); } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.datepicker * @description Module for the datepicker component. */ angular.module('material.components.datepicker', [ 'material.core', 'material.components.icon', 'material.components.virtualRepeat' ]); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.dialog */ MdDialogDirective.$inject = ["$$rAF", "$mdTheming", "$mdDialog"]; MdDialogProvider.$inject = ["$$interimElementProvider"]; angular .module('material.components.dialog', [ 'material.core', 'material.components.backdrop' ]) .directive('mdDialog', MdDialogDirective) .provider('$mdDialog', MdDialogProvider); /** * @ngdoc directive * @name mdDialog * @module material.components.dialog * * @restrict E * * @description * `` - The dialog's template must be inside this element. * * Inside, use an `` element for the dialog's content, and use * an `` element for the dialog's actions. * * ## CSS * - `.md-dialog-content` - class that sets the padding on the content as the spec file * * ## Notes * - If you specify an `id` for the ``, the `` will have the same `id` * prefixed with `dialogContent_`. * * @usage * ### Dialog template * * * * * *

Number {{item}}

*
*
*
* * Close Dialog * *
*
*/ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) { return { restrict: 'E', link: function(scope, element) { element.addClass('_md'); // private md component indicator for styling $mdTheming(element); $$rAF(function() { var images; var content = element[0].querySelector('md-dialog-content'); if (content) { images = content.getElementsByTagName('img'); addOverflowClass(); //-- delayed image loading may impact scroll height, check after images are loaded angular.element(images).on('load', addOverflowClass); } scope.$on('$destroy', function() { $mdDialog.destroy(element); }); /** * */ function addOverflowClass() { element.toggleClass('md-content-overflow', content.scrollHeight > content.clientHeight); } }); } }; } /** * @ngdoc service * @name $mdDialog * @module material.components.dialog * * @description * `$mdDialog` opens a dialog over the app to inform users about critical information or require * them to make decisions. There are two approaches for setup: a simple promise API * and regular object syntax. * * ## Restrictions * * - The dialog is always given an isolate scope. * - The dialog's template must have an outer `` element. * Inside, use an `` element for the dialog's content, and use * an `` element for the dialog's actions. * - Dialogs must cover the entire application to keep interactions inside of them. * Use the `parent` option to change where dialogs are appended. * * ## Sizing * - Complex dialogs can be sized with `flex="percentage"`, i.e. `flex="66"`. * - Default max-width is 80% of the `rootElement` or `parent`. * * ## CSS * - `.md-dialog-content` - class that sets the padding on the content as the spec file * * @usage * *
*
* * Employee Alert! * *
*
* * Custom Dialog * *
*
* * Close Alert * *
*
* * Greet Employee * *
*
*
* * ### JavaScript: object syntax * * (function(angular, undefined){ * "use strict"; * * angular * .module('demoApp', ['ngMaterial']) * .controller('AppCtrl', AppController); * * function AppController($scope, $mdDialog) { * var alert; * $scope.showAlert = showAlert; * $scope.showDialog = showDialog; * $scope.items = [1, 2, 3]; * * // Internal method * function showAlert() { * alert = $mdDialog.alert({ * title: 'Attention', * textContent: 'This is an example of how easy dialogs can be!', * ok: 'Close' * }); * * $mdDialog * .show( alert ) * .finally(function() { * alert = undefined; * }); * } * * function showDialog($event) { * var parentEl = angular.element(document.body); * $mdDialog.show({ * parent: parentEl, * targetEvent: $event, * template: * '' + * ' '+ * ' '+ * ' '+ * '

Number {{item}}

' + * ' '+ * '
'+ * '
' + * ' ' + * ' ' + * ' Close Dialog' + * ' ' + * ' ' + * '
', * locals: { * items: $scope.items * }, * controller: DialogController * }); * function DialogController($scope, $mdDialog, items) { * $scope.items = items; * $scope.closeDialog = function() { * $mdDialog.hide(); * } * } * } * } * })(angular); *
* * ### Multiple Dialogs * Using the `multiple` option for the `$mdDialog` service allows developers to show multiple dialogs * at the same time. * * * // From plain options * $mdDialog.show({ * multiple: true * }); * * // From a dialog preset * $mdDialog.show( * $mdDialog * .alert() * .multiple(true) * ); * * * * ### Pre-Rendered Dialogs * By using the `contentElement` option, it is possible to use an already existing element in the DOM. * * > Pre-rendered dialogs will be not linked to any scope and will not instantiate any new controller.
* > You can manually link the elements to a scope or instantiate a controller from the template (`ng-controller`) * * * $scope.showPrerenderedDialog = function() { * $mdDialog.show({ * contentElement: '#myStaticDialog', * parent: angular.element(document.body) * }); * }; * * * When using a string as value, `$mdDialog` will automatically query the DOM for the specified CSS selector. * * *
*
* * This is a pre-rendered dialog. * *
*
*
* * **Notice**: It is important, to use the `.md-dialog-container` as the content element, otherwise the dialog * will not show up. * * It also possible to use a DOM element for the `contentElement` option. * - `contentElement: document.querySelector('#myStaticDialog')` * - `contentElement: angular.element(TEMPLATE)` * * When using a `template` as content element, it will be not compiled upon open. * This allows you to compile the element yourself and use it each time the dialog opens. * * ### Custom Presets * Developers are also able to create their own preset, which can be easily used without repeating * their options each time. * * * $mdDialogProvider.addPreset('testPreset', { * options: function() { * return { * template: * '' + * 'This is a custom preset' + * '', * controllerAs: 'dialog', * bindToController: true, * clickOutsideToClose: true, * escapeToClose: true * }; * } * }); * * * After you created your preset at config phase, you can easily access it. * * * $mdDialog.show( * $mdDialog.testPreset() * ); * * * ### JavaScript: promise API syntax, custom dialog template * * (function(angular, undefined){ * "use strict"; * * angular * .module('demoApp', ['ngMaterial']) * .controller('EmployeeController', EmployeeEditor) * .controller('GreetingController', GreetingController); * * // Fictitious Employee Editor to show how to use simple and complex dialogs. * * function EmployeeEditor($scope, $mdDialog) { * var alert; * * $scope.showAlert = showAlert; * $scope.closeAlert = closeAlert; * $scope.showGreeting = showCustomGreeting; * * $scope.hasAlert = function() { return !!alert }; * $scope.userName = $scope.userName || 'Bobby'; * * // Dialog #1 - Show simple alert dialog and cache * // reference to dialog instance * * function showAlert() { * alert = $mdDialog.alert() * .title('Attention, ' + $scope.userName) * .textContent('This is an example of how easy dialogs can be!') * .ok('Close'); * * $mdDialog * .show( alert ) * .finally(function() { * alert = undefined; * }); * } * * // Close the specified dialog instance and resolve with 'finished' flag * // Normally this is not needed, just use '$mdDialog.hide()' to close * // the most recent dialog popup. * * function closeAlert() { * $mdDialog.hide( alert, "finished" ); * alert = undefined; * } * * // Dialog #2 - Demonstrate more complex dialogs construction and popup. * * function showCustomGreeting($event) { * $mdDialog.show({ * targetEvent: $event, * template: * '' + * * ' Hello {{ employee }}!' + * * ' ' + * ' ' + * ' Close Greeting' + * ' ' + * ' ' + * '', * controller: 'GreetingController', * onComplete: afterShowAnimation, * locals: { employee: $scope.userName } * }); * * // When the 'enter' animation finishes... * * function afterShowAnimation(scope, element, options) { * // post-show code here: DOM element focus, etc. * } * } * * // Dialog #3 - Demonstrate use of ControllerAs and passing $scope to dialog * // Here we used ng-controller="GreetingController as vm" and * // $scope.vm === * * function showCustomGreeting() { * * $mdDialog.show({ * clickOutsideToClose: true, * * scope: $scope, // use parent scope in template * preserveScope: true, // do not forget this if use parent scope * // Since GreetingController is instantiated with ControllerAs syntax * // AND we are passing the parent '$scope' to the dialog, we MUST * // use 'vm.' in the template markup * * template: '' + * ' ' + * ' Hi There {{vm.employee}}' + * ' ' + * '', * * controller: function DialogController($scope, $mdDialog) { * $scope.closeDialog = function() { * $mdDialog.hide(); * } * } * }); * } * * } * * // Greeting controller used with the more complex 'showCustomGreeting()' custom dialog * * function GreetingController($scope, $mdDialog, employee) { * // Assigned from construction locals options... * $scope.employee = employee; * * $scope.closeDialog = function() { * // Easily hides most recent dialog shown... * // no specific instance reference is needed. * $mdDialog.hide(); * }; * } * * })(angular); * */ /** * @ngdoc method * @name $mdDialog#alert * * @description * Builds a preconfigured dialog with the specified message. * * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods: * * - $mdDialogPreset#title(string) - Sets the alert title. * - $mdDialogPreset#textContent(string) - Sets the alert message. * - $mdDialogPreset#htmlContent(string) - Sets the alert message as HTML. Requires ngSanitize * module to be loaded. HTML is not run through Angular's compiler. * - $mdDialogPreset#ok(string) - Sets the alert "Okay" button text. * - $mdDialogPreset#theme(string) - Sets the theme of the alert dialog. * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * */ /** * @ngdoc method * @name $mdDialog#confirm * * @description * Builds a preconfigured dialog with the specified message. You can call show and the promise returned * will be resolved only if the user clicks the confirm action on the dialog. * * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods: * * Additionally, it supports the following methods: * * - $mdDialogPreset#title(string) - Sets the confirm title. * - $mdDialogPreset#textContent(string) - Sets the confirm message. * - $mdDialogPreset#htmlContent(string) - Sets the confirm message as HTML. Requires ngSanitize * module to be loaded. HTML is not run through Angular's compiler. * - $mdDialogPreset#ok(string) - Sets the confirm "Okay" button text. * - $mdDialogPreset#cancel(string) - Sets the confirm "Cancel" button text. * - $mdDialogPreset#theme(string) - Sets the theme of the confirm dialog. * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * */ /** * @ngdoc method * @name $mdDialog#prompt * * @description * Builds a preconfigured dialog with the specified message and input box. You can call show and the promise returned * will be resolved only if the user clicks the prompt action on the dialog, passing the input value as the first argument. * * @returns {obj} an `$mdDialogPreset` with the chainable configuration methods: * * Additionally, it supports the following methods: * * - $mdDialogPreset#title(string) - Sets the prompt title. * - $mdDialogPreset#textContent(string) - Sets the prompt message. * - $mdDialogPreset#htmlContent(string) - Sets the prompt message as HTML. Requires ngSanitize * module to be loaded. HTML is not run through Angular's compiler. * - $mdDialogPreset#placeholder(string) - Sets the placeholder text for the input. * - $mdDialogPreset#initialValue(string) - Sets the initial value for the prompt input. * - $mdDialogPreset#ok(string) - Sets the prompt "Okay" button text. * - $mdDialogPreset#cancel(string) - Sets the prompt "Cancel" button text. * - $mdDialogPreset#theme(string) - Sets the theme of the prompt dialog. * - $mdDialogPreset#targetEvent(DOMClickEvent=) - A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * */ /** * @ngdoc method * @name $mdDialog#show * * @description * Show a dialog with the specified options. * * @param {object} optionsOrPreset Either provide an `$mdDialogPreset` returned from `alert()`, and * `confirm()`, or an options object with the following properties: * - `templateUrl` - `{string=}`: The url of a template that will be used as the content * of the dialog. * - `template` - `{string=}`: HTML template to show in the dialog. This **must** be trusted HTML * with respect to Angular's [$sce service](https://docs.angularjs.org/api/ng/service/$sce). * This template should **never** be constructed with any kind of user input or user data. * - `contentElement` - `{string|Element}`: Instead of using a template, which will be compiled each time a * dialog opens, you can also use a DOM element.
* * When specifying an element, which is present on the DOM, `$mdDialog` will temporary fetch the element into * the dialog and restores it at the old DOM position upon close. * * When specifying a string, the string be used as a CSS selector, to lookup for the element in the DOM. * - `autoWrap` - `{boolean=}`: Whether or not to automatically wrap the template with a * `` tag if one is not provided. Defaults to true. Can be disabled if you provide a * custom dialog directive. * - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option, * the location of the click will be used as the starting point for the opening animation * of the the dialog. * - `openFrom` - `{string|Element|object}`: The query selector, DOM element or the Rect object * that is used to determine the bounds (top, left, height, width) from which the Dialog will * originate. * - `closeTo` - `{string|Element|object}`: The query selector, DOM element or the Rect object * that is used to determine the bounds (top, left, height, width) to which the Dialog will * target. * - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified, * it will create a new isolate scope. * This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true. * - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. Default is false * - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the dialog is open. * Default true. * - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop behind the dialog. * Default true. * - `clickOutsideToClose` - `{boolean=}`: Whether the user can click outside the dialog to * close it. Default false. * - `escapeToClose` - `{boolean=}`: Whether the user can press escape to close the dialog. * Default true. * - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on open. Only disable if * focusing some other way, as focus management is required for dialogs to be accessible. * Defaults to true. * - `controller` - `{function|string=}`: The controller to associate with the dialog. The controller * will be injected with the local `$mdDialog`, which passes along a scope for the dialog. * - `locals` - `{object=}`: An object containing key/value pairs. The keys will be used as names * of values to inject into the controller. For example, `locals: {three: 3}` would inject * `three` into the controller, with the value 3. If `bindToController` is true, they will be * copied to the controller instead. * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in. * - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope. * - `parent` - `{element=}`: The element to append the dialog to. Defaults to appending * to the root element of the application. * - `onShowing` - `function(scope, element)`: Callback function used to announce the show() action is * starting. * - `onComplete` - `function(scope, element)`: Callback function used to announce when the show() action is * finished. * - `onRemoving` - `function(element, removePromise)`: Callback function used to announce the * close/hide() action is starting. This allows developers to run custom animations * in parallel the close animations. * - `fullscreen` `{boolean=}`: An option to toggle whether the dialog should show in fullscreen * or not. Defaults to `false`. * - `multiple` `{boolean=}`: An option to allow this dialog to display over one that's currently open. * @returns {promise} A promise that can be resolved with `$mdDialog.hide()` or * rejected with `$mdDialog.cancel()`. */ /** * @ngdoc method * @name $mdDialog#hide * * @description * Hide an existing dialog and resolve the promise returned from `$mdDialog.show()`. * * @param {*=} response An argument for the resolved promise. * * @returns {promise} A promise that is resolved when the dialog has been closed. */ /** * @ngdoc method * @name $mdDialog#cancel * * @description * Hide an existing dialog and reject the promise returned from `$mdDialog.show()`. * * @param {*=} response An argument for the rejected promise. * * @returns {promise} A promise that is resolved when the dialog has been closed. */ function MdDialogProvider($$interimElementProvider) { // Elements to capture and redirect focus when the user presses tab at the dialog boundary. MdDialogController.$inject = ["$mdDialog", "$mdConstant"]; dialogDefaultOptions.$inject = ["$mdDialog", "$mdAria", "$mdUtil", "$mdConstant", "$animate", "$document", "$window", "$rootElement", "$log", "$injector", "$mdTheming", "$interpolate", "$mdInteraction"]; var topFocusTrap, bottomFocusTrap; return $$interimElementProvider('$mdDialog') .setDefaults({ methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent', 'closeTo', 'openFrom', 'parent', 'fullscreen', 'multiple'], options: dialogDefaultOptions }) .addPreset('alert', { methods: ['title', 'htmlContent', 'textContent', 'content', 'ariaLabel', 'ok', 'theme', 'css'], options: advancedDialogOptions }) .addPreset('confirm', { methods: ['title', 'htmlContent', 'textContent', 'content', 'ariaLabel', 'ok', 'cancel', 'theme', 'css'], options: advancedDialogOptions }) .addPreset('prompt', { methods: ['title', 'htmlContent', 'textContent', 'initialValue', 'content', 'placeholder', 'ariaLabel', 'ok', 'cancel', 'theme', 'css', 'required'], options: advancedDialogOptions }); /* @ngInject */ function advancedDialogOptions() { return { template: [ '', ' ', '

{{ dialog.title }}

', '
', '
', '

{{::dialog.mdTextContent}}

', '
', ' ', ' ', ' ', '
', ' ', ' ', ' {{ dialog.cancel }}', ' ', ' ', ' {{ dialog.ok }}', ' ', ' ', '
' ].join('').replace(/\s\s+/g, ''), controller: MdDialogController, controllerAs: 'dialog', bindToController: true, }; } /** * Controller for the md-dialog interim elements * @ngInject */ function MdDialogController($mdDialog, $mdConstant) { // For compatibility with AngularJS 1.6+, we should always use the $onInit hook in // interimElements. The $mdCompiler simulates the $onInit hook for all versions. this.$onInit = function() { var isPrompt = this.$type == 'prompt'; if (isPrompt && this.initialValue) { this.result = this.initialValue; } this.hide = function() { $mdDialog.hide(isPrompt ? this.result : true); }; this.abort = function() { $mdDialog.cancel(); }; this.keypress = function($event) { if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) { $mdDialog.hide(this.result); } }; }; } /* @ngInject */ function dialogDefaultOptions($mdDialog, $mdAria, $mdUtil, $mdConstant, $animate, $document, $window, $rootElement, $log, $injector, $mdTheming, $interpolate, $mdInteraction) { return { hasBackdrop: true, isolateScope: true, onCompiling: beforeCompile, onShow: onShow, onShowing: beforeShow, onRemove: onRemove, clickOutsideToClose: false, escapeToClose: true, targetEvent: null, closeTo: null, openFrom: null, focusOnOpen: true, disableParentScroll: true, autoWrap: true, fullscreen: false, transformTemplate: function(template, options) { // Make the dialog container focusable, because otherwise the focus will be always redirected to // an element outside of the container, and the focus trap won't work probably.. // Also the tabindex is needed for the `escapeToClose` functionality, because // the keyDown event can't be triggered when the focus is outside of the container. var startSymbol = $interpolate.startSymbol(); var endSymbol = $interpolate.endSymbol(); var theme = startSymbol + (options.themeWatch ? '' : '::') + 'theme' + endSymbol; return '
' + validatedTemplate(template) + '
'; /** * The specified template should contain a wrapper element.... */ function validatedTemplate(template) { if (options.autoWrap && !/<\/md-dialog>/g.test(template)) { return '' + (template || '') + ''; } else { return template || ''; } } } }; function beforeCompile(options) { // Automatically apply the theme, if the user didn't specify a theme explicitly. // Those option changes need to be done, before the compilation has started, because otherwise // the option changes will be not available in the $mdCompilers locales. options.defaultTheme = $mdTheming.defaultTheme(); detectTheming(options); } function beforeShow(scope, element, options, controller) { if (controller) { var mdHtmlContent = controller.htmlContent || options.htmlContent || ''; var mdTextContent = controller.textContent || options.textContent || controller.content || options.content || ''; if (mdHtmlContent && !$injector.has('$sanitize')) { throw Error('The ngSanitize module must be loaded in order to use htmlContent.'); } if (mdHtmlContent && mdTextContent) { throw Error('md-dialog cannot have both `htmlContent` and `textContent`'); } // Only assign the content if nothing throws, otherwise it'll still be compiled. controller.mdHtmlContent = mdHtmlContent; controller.mdTextContent = mdTextContent; } } /** Show method for dialogs */ function onShow(scope, element, options, controller) { angular.element($document[0].body).addClass('md-dialog-is-showing'); var dialogElement = element.find('md-dialog'); // Once a dialog has `ng-cloak` applied on his template the dialog animation will not work properly. // This is a very common problem, so we have to notify the developer about this. if (dialogElement.hasClass('ng-cloak')) { var message = '$mdDialog: using `` will affect the dialog opening animations.'; $log.warn( message, element[0] ); } captureParentAndFromToElements(options); configureAria(dialogElement, options); showBackdrop(scope, element, options); activateListeners(element, options); return dialogPopIn(element, options) .then(function() { lockScreenReader(element, options); warnDeprecatedActions(); focusOnOpen(); }); /** * Check to see if they used the deprecated .md-actions class and log a warning */ function warnDeprecatedActions() { if (element[0].querySelector('.md-actions')) { $log.warn('Using a class of md-actions is deprecated, please use .'); } } /** * For alerts, focus on content... otherwise focus on * the close button (or equivalent) */ function focusOnOpen() { if (options.focusOnOpen) { var target = $mdUtil.findFocusTarget(element) || findCloseButton() || dialogElement; target.focus(); } /** * If no element with class dialog-close, try to find the last * button child in md-actions and assume it is a close button. * * If we find no actions at all, log a warning to the console. */ function findCloseButton() { return element[0].querySelector('.dialog-close, md-dialog-actions button:last-child'); } } } /** * Remove function for all dialogs */ function onRemove(scope, element, options) { options.deactivateListeners(); options.unlockScreenReader(); options.hideBackdrop(options.$destroy); // Remove the focus traps that we added earlier for keeping focus within the dialog. if (topFocusTrap && topFocusTrap.parentNode) { topFocusTrap.parentNode.removeChild(topFocusTrap); } if (bottomFocusTrap && bottomFocusTrap.parentNode) { bottomFocusTrap.parentNode.removeChild(bottomFocusTrap); } // For navigation $destroy events, do a quick, non-animated removal, // but for normal closes (from clicks, etc) animate the removal return !!options.$destroy ? detachAndClean() : animateRemoval().then( detachAndClean ); /** * For normal closes, animate the removal. * For forced closes (like $destroy events), skip the animations */ function animateRemoval() { return dialogPopOut(element, options); } /** * Detach the element */ function detachAndClean() { angular.element($document[0].body).removeClass('md-dialog-is-showing'); // Reverse the container stretch if using a content element. if (options.contentElement) { options.reverseContainerStretch(); } // Exposed cleanup function from the $mdCompiler. options.cleanupElement(); // Restores the focus to the origin element if the last interaction upon opening was a keyboard. if (!options.$destroy && options.originInteraction === 'keyboard') { options.origin.focus(); } } } function detectTheming(options) { // Once the user specifies a targetEvent, we will automatically try to find the correct // nested theme. var targetEl; if (options.targetEvent && options.targetEvent.target) { targetEl = angular.element(options.targetEvent.target); } var themeCtrl = targetEl && targetEl.controller('mdTheme'); if (!themeCtrl) { return; } options.themeWatch = themeCtrl.$shouldWatch; var theme = options.theme || themeCtrl.$mdTheme; if (theme) { options.scope.theme = theme; } var unwatch = themeCtrl.registerChanges(function (newTheme) { options.scope.theme = newTheme; if (!options.themeWatch) { unwatch(); } }); } /** * Capture originator/trigger/from/to element information (if available) * and the parent container for the dialog; defaults to the $rootElement * unless overridden in the options.parent */ function captureParentAndFromToElements(options) { options.origin = angular.extend({ element: null, bounds: null, focus: angular.noop }, options.origin || {}); options.parent = getDomElement(options.parent, $rootElement); options.closeTo = getBoundingClientRect(getDomElement(options.closeTo)); options.openFrom = getBoundingClientRect(getDomElement(options.openFrom)); if ( options.targetEvent ) { options.origin = getBoundingClientRect(options.targetEvent.target, options.origin); options.originInteraction = $mdInteraction.getLastInteractionType(); } /** * Identify the bounding RECT for the target element * */ function getBoundingClientRect (element, orig) { var source = angular.element((element || {})); if (source && source.length) { // Compute and save the target element's bounding rect, so that if the // element is hidden when the dialog closes, we can shrink the dialog // back to the same position it expanded from. // // Checking if the source is a rect object or a DOM element var bounds = {top:0,left:0,height:0,width:0}; var hasFn = angular.isFunction(source[0].getBoundingClientRect); return angular.extend(orig || {}, { element : hasFn ? source : undefined, bounds : hasFn ? source[0].getBoundingClientRect() : angular.extend({}, bounds, source[0]), focus : angular.bind(source, source.focus), }); } } /** * If the specifier is a simple string selector, then query for * the DOM element. */ function getDomElement(element, defaultElement) { if (angular.isString(element)) { element = $document[0].querySelector(element); } // If we have a reference to a raw dom element, always wrap it in jqLite return angular.element(element || defaultElement); } } /** * Listen for escape keys and outside clicks to auto close */ function activateListeners(element, options) { var window = angular.element($window); var onWindowResize = $mdUtil.debounce(function() { stretchDialogContainerToViewport(element, options); }, 60); var removeListeners = []; var smartClose = function() { // Only 'confirm' dialogs have a cancel button... escape/clickOutside will // cancel or fallback to hide. var closeFn = ( options.$type == 'alert' ) ? $mdDialog.hide : $mdDialog.cancel; $mdUtil.nextTick(closeFn, true); }; if (options.escapeToClose) { var parentTarget = options.parent; var keyHandlerFn = function(ev) { if (ev.keyCode === $mdConstant.KEY_CODE.ESCAPE) { ev.stopPropagation(); ev.preventDefault(); smartClose(); } }; // Add keydown listeners element.on('keydown', keyHandlerFn); parentTarget.on('keydown', keyHandlerFn); // Queue remove listeners function removeListeners.push(function() { element.off('keydown', keyHandlerFn); parentTarget.off('keydown', keyHandlerFn); }); } // Register listener to update dialog on window resize window.on('resize', onWindowResize); removeListeners.push(function() { window.off('resize', onWindowResize); }); if (options.clickOutsideToClose) { var target = element; var sourceElem; // Keep track of the element on which the mouse originally went down // so that we can only close the backdrop when the 'click' started on it. // A simple 'click' handler does not work, // it sets the target object as the element the mouse went down on. var mousedownHandler = function(ev) { sourceElem = ev.target; }; // We check if our original element and the target is the backdrop // because if the original was the backdrop and the target was inside the dialog // we don't want to dialog to close. var mouseupHandler = function(ev) { if (sourceElem === target[0] && ev.target === target[0]) { ev.stopPropagation(); ev.preventDefault(); smartClose(); } }; // Add listeners target.on('mousedown', mousedownHandler); target.on('mouseup', mouseupHandler); // Queue remove listeners function removeListeners.push(function() { target.off('mousedown', mousedownHandler); target.off('mouseup', mouseupHandler); }); } // Attach specific `remove` listener handler options.deactivateListeners = function() { removeListeners.forEach(function(removeFn) { removeFn(); }); options.deactivateListeners = null; }; } /** * Show modal backdrop element... */ function showBackdrop(scope, element, options) { if (options.disableParentScroll) { // !! DO this before creating the backdrop; since disableScrollAround() // configures the scroll offset; which is used by mdBackDrop postLink() options.restoreScroll = $mdUtil.disableScrollAround(element, options.parent); } if (options.hasBackdrop) { options.backdrop = $mdUtil.createBackdrop(scope, "md-dialog-backdrop md-opaque"); $animate.enter(options.backdrop, options.parent); } /** * Hide modal backdrop element... */ options.hideBackdrop = function hideBackdrop($destroy) { if (options.backdrop) { if ( !!$destroy ) options.backdrop.remove(); else $animate.leave(options.backdrop); } if (options.disableParentScroll) { options.restoreScroll && options.restoreScroll(); delete options.restoreScroll; } options.hideBackdrop = null; }; } /** * Inject ARIA-specific attributes appropriate for Dialogs */ function configureAria(element, options) { var role = (options.$type === 'alert') ? 'alertdialog' : 'dialog'; var dialogContent = element.find('md-dialog-content'); var existingDialogId = element.attr('id'); var dialogContentId = 'dialogContent_' + (existingDialogId || $mdUtil.nextUid()); element.attr({ 'role': role, 'tabIndex': '-1' }); if (dialogContent.length === 0) { dialogContent = element; // If the dialog element already had an ID, don't clobber it. if (existingDialogId) { dialogContentId = existingDialogId; } } dialogContent.attr('id', dialogContentId); element.attr('aria-describedby', dialogContentId); if (options.ariaLabel) { $mdAria.expect(element, 'aria-label', options.ariaLabel); } else { $mdAria.expectAsync(element, 'aria-label', function() { // If dialog title is specified, set aria-label with it // See https://github.com/angular/material/issues/10582 if (options.title) { return options.title; } else { var words = dialogContent.text().split(/\s+/); if (words.length > 3) words = words.slice(0, 3).concat('...'); return words.join(' '); } }); } // Set up elements before and after the dialog content to capture focus and // redirect back into the dialog. topFocusTrap = document.createElement('div'); topFocusTrap.classList.add('md-dialog-focus-trap'); topFocusTrap.tabIndex = 0; bottomFocusTrap = topFocusTrap.cloneNode(false); // When focus is about to move out of the dialog, we want to intercept it and redirect it // back to the dialog element. var focusHandler = function() { element.focus(); }; topFocusTrap.addEventListener('focus', focusHandler); bottomFocusTrap.addEventListener('focus', focusHandler); // The top focus trap inserted immeidately before the md-dialog element (as a sibling). // The bottom focus trap is inserted at the very end of the md-dialog element (as a child). element[0].parentNode.insertBefore(topFocusTrap, element[0]); element.after(bottomFocusTrap); } /** * Prevents screen reader interaction behind modal window * on swipe interfaces */ function lockScreenReader(element, options) { var isHidden = true; // get raw DOM node walkDOM(element[0]); options.unlockScreenReader = function() { isHidden = false; walkDOM(element[0]); options.unlockScreenReader = null; }; /** * Walk DOM to apply or remove aria-hidden on sibling nodes * and parent sibling nodes * */ function walkDOM(element) { while (element.parentNode) { if (element === document.body) { return; } var children = element.parentNode.children; for (var i = 0; i < children.length; i++) { // skip over child if it is an ascendant of the dialog // or a script or style tag if (element !== children[i] && !isNodeOneOf(children[i], ['SCRIPT', 'STYLE']) && !children[i].hasAttribute('aria-live')) { children[i].setAttribute('aria-hidden', isHidden); } } walkDOM(element = element.parentNode); } } } /** * Ensure the dialog container fill-stretches to the viewport */ function stretchDialogContainerToViewport(container, options) { var isFixed = $window.getComputedStyle($document[0].body).position == 'fixed'; var backdrop = options.backdrop ? $window.getComputedStyle(options.backdrop[0]) : null; var height = backdrop ? Math.min($document[0].body.clientHeight, Math.ceil(Math.abs(parseInt(backdrop.height, 10)))) : 0; var previousStyles = { top: container.css('top'), height: container.css('height') }; // If the body is fixed, determine the distance to the viewport in relative from the parent. var parentTop = Math.abs(options.parent[0].getBoundingClientRect().top); container.css({ top: (isFixed ? parentTop : 0) + 'px', height: height ? height + 'px' : '100%' }); return function() { // Reverts the modified styles back to the previous values. // This is needed for contentElements, which should have the same styles after close // as before. container.css(previousStyles); }; } /** * Dialog open and pop-in animation */ function dialogPopIn(container, options) { // Add the `md-dialog-container` to the DOM options.parent.append(container); options.reverseContainerStretch = stretchDialogContainerToViewport(container, options); var dialogEl = container.find('md-dialog'); var animator = $mdUtil.dom.animator; var buildTranslateToOrigin = animator.calculateZoomToOrigin; var translateOptions = {transitionInClass: 'md-transition-in', transitionOutClass: 'md-transition-out'}; var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.openFrom || options.origin)); var to = animator.toTransformCss(""); // defaults to center display (or parent or $rootElement) dialogEl.toggleClass('md-dialog-fullscreen', !!options.fullscreen); return animator .translate3d(dialogEl, from, to, translateOptions) .then(function(animateReversal) { // Build a reversal translate function synced to this translation... options.reverseAnimate = function() { delete options.reverseAnimate; if (options.closeTo) { // Using the opposite classes to create a close animation to the closeTo element translateOptions = {transitionInClass: 'md-transition-out', transitionOutClass: 'md-transition-in'}; from = to; to = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.closeTo)); return animator .translate3d(dialogEl, from, to,translateOptions); } return animateReversal( to = animator.toTransformCss( // in case the origin element has moved or is hidden, // let's recalculate the translateCSS buildTranslateToOrigin(dialogEl, options.origin) ) ); }; // Function to revert the generated animation styles on the dialog element. // Useful when using a contentElement instead of a template. options.clearAnimate = function() { delete options.clearAnimate; // Remove the transition classes, added from $animateCSS, since those can't be removed // by reversely running the animator. dialogEl.removeClass([ translateOptions.transitionOutClass, translateOptions.transitionInClass ].join(' ')); // Run the animation reversely to remove the previous added animation styles. return animator.translate3d(dialogEl, to, animator.toTransformCss(''), {}); }; return true; }); } /** * Dialog close and pop-out animation */ function dialogPopOut(container, options) { return options.reverseAnimate().then(function() { if (options.contentElement) { // When we use a contentElement, we want the element to be the same as before. // That means, that we have to clear all the animation properties, like transform. options.clearAnimate(); } }); } /** * Utility function to filter out raw DOM nodes */ function isNodeOneOf(elem, nodeTypeArray) { if (nodeTypeArray.indexOf(elem.nodeName) !== -1) { return true; } } } } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.divider * @description Divider module! */ MdDividerDirective.$inject = ["$mdTheming"]; angular.module('material.components.divider', [ 'material.core' ]) .directive('mdDivider', MdDividerDirective); /** * @ngdoc directive * @name mdDivider * @module material.components.divider * @restrict E * * @description * Dividers group and separate content within lists and page layouts using strong visual and spatial distinctions. This divider is a thin rule, lightweight enough to not distract the user from content. * * @param {boolean=} md-inset Add this attribute to activate the inset divider style. * @usage * * * * * * */ function MdDividerDirective($mdTheming) { return { restrict: 'E', link: $mdTheming }; } })(); (function(){ "use strict"; (function() { 'use strict'; /** * @ngdoc module * @name material.components.fabActions */ MdFabActionsDirective.$inject = ["$mdUtil"]; angular .module('material.components.fabActions', ['material.core']) .directive('mdFabActions', MdFabActionsDirective); /** * @ngdoc directive * @name mdFabActions * @module material.components.fabActions * * @restrict E * * @description * The `` directive is used inside of a `` or * `` directive to mark an element (or elements) as the actions and setup the * proper event listeners. * * @usage * See the `` or `` directives for example usage. */ function MdFabActionsDirective($mdUtil) { return { restrict: 'E', require: ['^?mdFabSpeedDial', '^?mdFabToolbar'], compile: function(element, attributes) { var children = element.children(); var hasNgRepeat = $mdUtil.prefixer().hasAttribute(children, 'ng-repeat'); // Support both ng-repeat and static content if (hasNgRepeat) { children.addClass('md-fab-action-item'); } else { // Wrap every child in a new div and add a class that we can scale/fling independently children.wrap('
'); } } }; } })(); })(); (function(){ "use strict"; (function() { 'use strict'; MdFabController.$inject = ["$scope", "$element", "$animate", "$mdUtil", "$mdConstant", "$timeout"]; angular.module('material.components.fabShared', ['material.core']) .controller('MdFabController', MdFabController); function MdFabController($scope, $element, $animate, $mdUtil, $mdConstant, $timeout) { var vm = this; var initialAnimationAttempts = 0; // NOTE: We use async eval(s) below to avoid conflicts with any existing digest loops vm.open = function() { $scope.$evalAsync("vm.isOpen = true"); }; vm.close = function() { // Async eval to avoid conflicts with existing digest loops $scope.$evalAsync("vm.isOpen = false"); // Focus the trigger when the element closes so users can still tab to the next item $element.find('md-fab-trigger')[0].focus(); }; // Toggle the open/close state when the trigger is clicked vm.toggle = function() { $scope.$evalAsync("vm.isOpen = !vm.isOpen"); }; /* * AngularJS Lifecycle hook for newer AngularJS versions. * Bindings are not guaranteed to have been assigned in the controller, but they are in the $onInit hook. */ vm.$onInit = function() { setupDefaults(); setupListeners(); setupWatchers(); fireInitialAnimations(); }; // For AngularJS 1.4 and older, where there are no lifecycle hooks but bindings are pre-assigned, // manually call the $onInit hook. if (angular.version.major === 1 && angular.version.minor <= 4) { this.$onInit(); } function setupDefaults() { // Set the default direction to 'down' if none is specified vm.direction = vm.direction || 'down'; // Set the default to be closed vm.isOpen = vm.isOpen || false; // Start the keyboard interaction at the first action resetActionIndex(); // Add an animations waiting class so we know not to run $element.addClass('md-animations-waiting'); } function setupListeners() { var eventTypes = [ 'click', 'focusin', 'focusout' ]; // Add our listeners angular.forEach(eventTypes, function(eventType) { $element.on(eventType, parseEvents); }); // Remove our listeners when destroyed $scope.$on('$destroy', function() { angular.forEach(eventTypes, function(eventType) { $element.off(eventType, parseEvents); }); // remove any attached keyboard handlers in case element is removed while // speed dial is open disableKeyboard(); }); } var closeTimeout; function parseEvents(event) { // If the event is a click, just handle it if (event.type == 'click') { handleItemClick(event); } // If we focusout, set a timeout to close the element if (event.type == 'focusout' && !closeTimeout) { closeTimeout = $timeout(function() { vm.close(); }, 100, false); } // If we see a focusin and there is a timeout about to run, cancel it so we stay open if (event.type == 'focusin' && closeTimeout) { $timeout.cancel(closeTimeout); closeTimeout = null; } } function resetActionIndex() { vm.currentActionIndex = -1; } function setupWatchers() { // Watch for changes to the direction and update classes/attributes $scope.$watch('vm.direction', function(newDir, oldDir) { // Add the appropriate classes so we can target the direction in the CSS $animate.removeClass($element, 'md-' + oldDir); $animate.addClass($element, 'md-' + newDir); // Reset the action index since it may have changed resetActionIndex(); }); var trigger, actions; // Watch for changes to md-open $scope.$watch('vm.isOpen', function(isOpen) { // Reset the action index since it may have changed resetActionIndex(); // We can't get the trigger/actions outside of the watch because the component hasn't been // linked yet, so we wait until the first watch fires to cache them. if (!trigger || !actions) { trigger = getTriggerElement(); actions = getActionsElement(); } if (isOpen) { enableKeyboard(); } else { disableKeyboard(); } var toAdd = isOpen ? 'md-is-open' : ''; var toRemove = isOpen ? '' : 'md-is-open'; // Set the proper ARIA attributes trigger.attr('aria-haspopup', true); trigger.attr('aria-expanded', isOpen); actions.attr('aria-hidden', !isOpen); // Animate the CSS classes $animate.setClass($element, toAdd, toRemove); }); } function fireInitialAnimations() { // If the element is actually visible on the screen if ($element[0].scrollHeight > 0) { // Fire our animation $animate.addClass($element, '_md-animations-ready').then(function() { // Remove the waiting class $element.removeClass('md-animations-waiting'); }); } // Otherwise, try for up to 1 second before giving up else if (initialAnimationAttempts < 10) { $timeout(fireInitialAnimations, 100); // Increment our counter initialAnimationAttempts = initialAnimationAttempts + 1; } } function enableKeyboard() { $element.on('keydown', keyPressed); // On the next tick, setup a check for outside clicks; we do this on the next tick to avoid // clicks/touches that result in the isOpen attribute changing (e.g. a bound radio button) $mdUtil.nextTick(function() { angular.element(document).on('click touchend', checkForOutsideClick); }); // TODO: On desktop, we should be able to reset the indexes so you cannot tab through, but // this breaks accessibility, especially on mobile, since you have no arrow keys to press //resetActionTabIndexes(); } function disableKeyboard() { $element.off('keydown', keyPressed); angular.element(document).off('click touchend', checkForOutsideClick); } function checkForOutsideClick(event) { if (event.target) { var closestTrigger = $mdUtil.getClosest(event.target, 'md-fab-trigger'); var closestActions = $mdUtil.getClosest(event.target, 'md-fab-actions'); if (!closestTrigger && !closestActions) { vm.close(); } } } function keyPressed(event) { switch (event.which) { case $mdConstant.KEY_CODE.ESCAPE: vm.close(); event.preventDefault(); return false; case $mdConstant.KEY_CODE.LEFT_ARROW: doKeyLeft(event); return false; case $mdConstant.KEY_CODE.UP_ARROW: doKeyUp(event); return false; case $mdConstant.KEY_CODE.RIGHT_ARROW: doKeyRight(event); return false; case $mdConstant.KEY_CODE.DOWN_ARROW: doKeyDown(event); return false; } } function doActionPrev(event) { focusAction(event, -1); } function doActionNext(event) { focusAction(event, 1); } function focusAction(event, direction) { var actions = resetActionTabIndexes(); // Increment/decrement the counter with restrictions vm.currentActionIndex = vm.currentActionIndex + direction; vm.currentActionIndex = Math.min(actions.length - 1, vm.currentActionIndex); vm.currentActionIndex = Math.max(0, vm.currentActionIndex); // Focus the element var focusElement = angular.element(actions[vm.currentActionIndex]).children()[0]; angular.element(focusElement).attr('tabindex', 0); focusElement.focus(); // Make sure the event doesn't bubble and cause something else event.preventDefault(); event.stopImmediatePropagation(); } function resetActionTabIndexes() { // Grab all of the actions var actions = getActionsElement()[0].querySelectorAll('.md-fab-action-item'); // Disable all other actions for tabbing angular.forEach(actions, function(action) { angular.element(angular.element(action).children()[0]).attr('tabindex', -1); }); return actions; } function doKeyLeft(event) { if (vm.direction === 'left') { doActionNext(event); } else { doActionPrev(event); } } function doKeyUp(event) { if (vm.direction === 'down') { doActionPrev(event); } else { doActionNext(event); } } function doKeyRight(event) { if (vm.direction === 'left') { doActionPrev(event); } else { doActionNext(event); } } function doKeyDown(event) { if (vm.direction === 'up') { doActionPrev(event); } else { doActionNext(event); } } function isTrigger(element) { return $mdUtil.getClosest(element, 'md-fab-trigger'); } function isAction(element) { return $mdUtil.getClosest(element, 'md-fab-actions'); } function handleItemClick(event) { if (isTrigger(event.target)) { vm.toggle(); } if (isAction(event.target)) { vm.close(); } } function getTriggerElement() { return $element.find('md-fab-trigger'); } function getActionsElement() { return $element.find('md-fab-actions'); } } })(); })(); (function(){ "use strict"; (function() { 'use strict'; /** * The duration of the CSS animation in milliseconds. * * @type {number} */ MdFabSpeedDialFlingAnimation.$inject = ["$timeout"]; MdFabSpeedDialScaleAnimation.$inject = ["$timeout"]; var cssAnimationDuration = 300; /** * @ngdoc module * @name material.components.fabSpeedDial */ angular // Declare our module .module('material.components.fabSpeedDial', [ 'material.core', 'material.components.fabShared', 'material.components.fabActions' ]) // Register our directive .directive('mdFabSpeedDial', MdFabSpeedDialDirective) // Register our custom animations .animation('.md-fling', MdFabSpeedDialFlingAnimation) .animation('.md-scale', MdFabSpeedDialScaleAnimation) // Register a service for each animation so that we can easily inject them into unit tests .service('mdFabSpeedDialFlingAnimation', MdFabSpeedDialFlingAnimation) .service('mdFabSpeedDialScaleAnimation', MdFabSpeedDialScaleAnimation); /** * @ngdoc directive * @name mdFabSpeedDial * @module material.components.fabSpeedDial * * @restrict E * * @description * The `` directive is used to present a series of popup elements (usually * ``s) for quick access to common actions. * * There are currently two animations available by applying one of the following classes to * the component: * * - `md-fling` - The speed dial items appear from underneath the trigger and move into their * appropriate positions. * - `md-scale` - The speed dial items appear in their proper places by scaling from 0% to 100%. * * You may also easily position the trigger by applying one one of the following classes to the * `` element: * - `md-fab-top-left` * - `md-fab-top-right` * - `md-fab-bottom-left` * - `md-fab-bottom-right` * * These CSS classes use `position: absolute`, so you need to ensure that the container element * also uses `position: absolute` or `position: relative` in order for them to work. * * Additionally, you may use the standard `ng-mouseenter` and `ng-mouseleave` directives to * open or close the speed dial. However, if you wish to allow users to hover over the empty * space where the actions will appear, you must also add the `md-hover-full` class to the speed * dial element. Without this, the hover effect will only occur on top of the trigger. * * See the demos for more information. * * ## Troubleshooting * * If your speed dial shows the closing animation upon launch, you may need to use `ng-cloak` on * the parent container to ensure that it is only visible once ready. We have plans to remove this * necessity in the future. * * @usage * * * * * * * * * * * * * * * * * * * @param {string} md-direction From which direction you would like the speed dial to appear * relative to the trigger element. * @param {expression=} md-open Programmatically control whether or not the speed-dial is visible. */ function MdFabSpeedDialDirective() { return { restrict: 'E', scope: { direction: '@?mdDirection', isOpen: '=?mdOpen' }, bindToController: true, controller: 'MdFabController', controllerAs: 'vm', link: FabSpeedDialLink }; function FabSpeedDialLink(scope, element) { // Prepend an element to hold our CSS variables so we can use them in the animations below element.prepend('
'); } } function MdFabSpeedDialFlingAnimation($timeout) { function delayDone(done) { $timeout(done, cssAnimationDuration, false); } function runAnimation(element) { // Don't run if we are still waiting and we are not ready if (element.hasClass('md-animations-waiting') && !element.hasClass('_md-animations-ready')) { return; } var el = element[0]; var ctrl = element.controller('mdFabSpeedDial'); var items = el.querySelectorAll('.md-fab-action-item'); // Grab our trigger element var triggerElement = el.querySelector('md-fab-trigger'); // Grab our element which stores CSS variables var variablesElement = el.querySelector('._md-css-variables'); // Setup JS variables based on our CSS variables var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex); // Always reset the items to their natural position/state angular.forEach(items, function(item, index) { var styles = item.style; styles.transform = styles.webkitTransform = ''; styles.transitionDelay = ''; styles.opacity = 1; // Make the items closest to the trigger have the highest z-index styles.zIndex = (items.length - index) + startZIndex; }); // Set the trigger to be above all of the actions so they disappear behind it. triggerElement.style.zIndex = startZIndex + items.length + 1; // If the control is closed, hide the items behind the trigger if (!ctrl.isOpen) { angular.forEach(items, function(item, index) { var newPosition, axis; var styles = item.style; // Make sure to account for differences in the dimensions of the trigger verses the items // so that we can properly center everything; this helps hide the item's shadows behind // the trigger. var triggerItemHeightOffset = (triggerElement.clientHeight - item.clientHeight) / 2; var triggerItemWidthOffset = (triggerElement.clientWidth - item.clientWidth) / 2; switch (ctrl.direction) { case 'up': newPosition = (item.scrollHeight * (index + 1) + triggerItemHeightOffset); axis = 'Y'; break; case 'down': newPosition = -(item.scrollHeight * (index + 1) + triggerItemHeightOffset); axis = 'Y'; break; case 'left': newPosition = (item.scrollWidth * (index + 1) + triggerItemWidthOffset); axis = 'X'; break; case 'right': newPosition = -(item.scrollWidth * (index + 1) + triggerItemWidthOffset); axis = 'X'; break; } var newTranslate = 'translate' + axis + '(' + newPosition + 'px)'; styles.transform = styles.webkitTransform = newTranslate; }); } } return { addClass: function(element, className, done) { if (element.hasClass('md-fling')) { runAnimation(element); delayDone(done); } else { done(); } }, removeClass: function(element, className, done) { runAnimation(element); delayDone(done); } }; } function MdFabSpeedDialScaleAnimation($timeout) { function delayDone(done) { $timeout(done, cssAnimationDuration, false); } var delay = 65; function runAnimation(element) { var el = element[0]; var ctrl = element.controller('mdFabSpeedDial'); var items = el.querySelectorAll('.md-fab-action-item'); // Grab our element which stores CSS variables var variablesElement = el.querySelector('._md-css-variables'); // Setup JS variables based on our CSS variables var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex); // Always reset the items to their natural position/state angular.forEach(items, function(item, index) { var styles = item.style, offsetDelay = index * delay; styles.opacity = ctrl.isOpen ? 1 : 0; styles.transform = styles.webkitTransform = ctrl.isOpen ? 'scale(1)' : 'scale(0)'; styles.transitionDelay = (ctrl.isOpen ? offsetDelay : (items.length - offsetDelay)) + 'ms'; // Make the items closest to the trigger have the highest z-index styles.zIndex = (items.length - index) + startZIndex; }); } return { addClass: function(element, className, done) { runAnimation(element); delayDone(done); }, removeClass: function(element, className, done) { runAnimation(element); delayDone(done); } }; } })(); })(); (function(){ "use strict"; (function() { 'use strict'; /** * @ngdoc module * @name material.components.fabToolbar */ angular // Declare our module .module('material.components.fabToolbar', [ 'material.core', 'material.components.fabShared', 'material.components.fabActions' ]) // Register our directive .directive('mdFabToolbar', MdFabToolbarDirective) // Register our custom animations .animation('.md-fab-toolbar', MdFabToolbarAnimation) // Register a service for the animation so that we can easily inject it into unit tests .service('mdFabToolbarAnimation', MdFabToolbarAnimation); /** * @ngdoc directive * @name mdFabToolbar * @module material.components.fabToolbar * * @restrict E * * @description * * The `` directive is used to present a toolbar of elements (usually ``s) * for quick access to common actions when a floating action button is activated (via click or * keyboard navigation). * * You may also easily position the trigger by applying one one of the following classes to the * `` element: * - `md-fab-top-left` * - `md-fab-top-right` * - `md-fab-bottom-left` * - `md-fab-bottom-right` * * These CSS classes use `position: absolute`, so you need to ensure that the container element * also uses `position: absolute` or `position: relative` in order for them to work. * * @usage * * * * * * * * * * * * * * * * * * * * * * @param {string} md-direction From which direction you would like the toolbar items to appear * relative to the trigger element. Supports `left` and `right` directions. * @param {expression=} md-open Programmatically control whether or not the toolbar is visible. */ function MdFabToolbarDirective() { return { restrict: 'E', transclude: true, template: '
' + '
' + '
', scope: { direction: '@?mdDirection', isOpen: '=?mdOpen' }, bindToController: true, controller: 'MdFabController', controllerAs: 'vm', link: link }; function link(scope, element, attributes) { // Add the base class for animations element.addClass('md-fab-toolbar'); // Prepend the background element to the trigger's button element.find('md-fab-trigger').find('button') .prepend('
'); } } function MdFabToolbarAnimation() { function runAnimation(element, className, done) { // If no className was specified, don't do anything if (!className) { return; } var el = element[0]; var ctrl = element.controller('mdFabToolbar'); // Grab the relevant child elements var backgroundElement = el.querySelector('.md-fab-toolbar-background'); var triggerElement = el.querySelector('md-fab-trigger button'); var toolbarElement = el.querySelector('md-toolbar'); var iconElement = el.querySelector('md-fab-trigger button md-icon'); var actions = element.find('md-fab-actions').children(); // If we have both elements, use them to position the new background if (triggerElement && backgroundElement) { // Get our variables var color = window.getComputedStyle(triggerElement).getPropertyValue('background-color'); var width = el.offsetWidth; var height = el.offsetHeight; // Make it twice as big as it should be since we scale from the center var scale = 2 * (width / triggerElement.offsetWidth); // Set some basic styles no matter what animation we're doing backgroundElement.style.backgroundColor = color; backgroundElement.style.borderRadius = width + 'px'; // If we're open if (ctrl.isOpen) { // Turn on toolbar pointer events when closed toolbarElement.style.pointerEvents = 'inherit'; backgroundElement.style.width = triggerElement.offsetWidth + 'px'; backgroundElement.style.height = triggerElement.offsetHeight + 'px'; backgroundElement.style.transform = 'scale(' + scale + ')'; // Set the next close animation to have the proper delays backgroundElement.style.transitionDelay = '0ms'; iconElement && (iconElement.style.transitionDelay = '.3s'); // Apply a transition delay to actions angular.forEach(actions, function(action, index) { action.style.transitionDelay = (actions.length - index) * 25 + 'ms'; }); } else { // Turn off toolbar pointer events when closed toolbarElement.style.pointerEvents = 'none'; // Scale it back down to the trigger's size backgroundElement.style.transform = 'scale(1)'; // Reset the position backgroundElement.style.top = '0'; if (element.hasClass('md-right')) { backgroundElement.style.left = '0'; backgroundElement.style.right = null; } if (element.hasClass('md-left')) { backgroundElement.style.right = '0'; backgroundElement.style.left = null; } // Set the next open animation to have the proper delays backgroundElement.style.transitionDelay = '200ms'; iconElement && (iconElement.style.transitionDelay = '0ms'); // Apply a transition delay to actions angular.forEach(actions, function(action, index) { action.style.transitionDelay = 200 + (index * 25) + 'ms'; }); } } } return { addClass: function(element, className, done) { runAnimation(element, className, done); done(); }, removeClass: function(element, className, done) { runAnimation(element, className, done); done(); } }; } })(); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.icon * @description * Icon */ angular.module('material.components.icon', ['material.core']); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.gridList */ GridListController.$inject = ["$mdUtil"]; GridLayoutFactory.$inject = ["$mdUtil"]; GridListDirective.$inject = ["$interpolate", "$mdConstant", "$mdGridLayout", "$mdMedia"]; GridTileDirective.$inject = ["$mdMedia"]; angular.module('material.components.gridList', ['material.core']) .directive('mdGridList', GridListDirective) .directive('mdGridTile', GridTileDirective) .directive('mdGridTileFooter', GridTileCaptionDirective) .directive('mdGridTileHeader', GridTileCaptionDirective) .factory('$mdGridLayout', GridLayoutFactory); /** * @ngdoc directive * @name mdGridList * @module material.components.gridList * @restrict E * @description * Grid lists are an alternative to standard list views. Grid lists are distinct * from grids used for layouts and other visual presentations. * * A grid list is best suited to presenting a homogenous data type, typically * images, and is optimized for visual comprehension and differentiating between * like data types. * * A grid list is a continuous element consisting of tessellated, regular * subdivisions called cells that contain tiles (`md-grid-tile`). * * Concept of grid explained visually * Grid concepts legend * * Cells are arrayed vertically and horizontally within the grid. * * Tiles hold content and can span one or more cells vertically or horizontally. * * ### Responsive Attributes * * The `md-grid-list` directive supports "responsive" attributes, which allow * different `md-cols`, `md-gutter` and `md-row-height` values depending on the * currently matching media query. * * In order to set a responsive attribute, first define the fallback value with * the standard attribute name, then add additional attributes with the * following convention: `{base-attribute-name}-{media-query-name}="{value}"` * (ie. `md-cols-lg="8"`) * * @param {number} md-cols Number of columns in the grid. * @param {string} md-row-height One of *
    *
  • CSS length - Fixed height rows (eg. `8px` or `1rem`)
  • *
  • `{width}:{height}` - Ratio of width to height (eg. * `md-row-height="16:9"`)
  • *
  • `"fit"` - Height will be determined by subdividing the available * height by the number of rows
  • *
* @param {string=} md-gutter The amount of space between tiles in CSS units * (default 1px) * @param {expression=} md-on-layout Expression to evaluate after layout. Event * object is available as `$event`, and contains performance information. * * @usage * Basic: * * * * * * * Fixed-height rows: * * * * * * * Fit rows: * * * * * * * Using responsive attributes: * * * * * */ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia) { return { restrict: 'E', controller: GridListController, scope: { mdOnLayout: '&' }, link: postLink }; function postLink(scope, element, attrs, ctrl) { element.addClass('_md'); // private md component indicator for styling // Apply semantics element.attr('role', 'list'); // Provide the controller with a way to trigger layouts. ctrl.layoutDelegate = layoutDelegate; var invalidateLayout = angular.bind(ctrl, ctrl.invalidateLayout), unwatchAttrs = watchMedia(); scope.$on('$destroy', unwatchMedia); /** * Watches for changes in media, invalidating layout as necessary. */ function watchMedia() { for (var mediaName in $mdConstant.MEDIA) { $mdMedia(mediaName); // initialize $mdMedia.getQuery($mdConstant.MEDIA[mediaName]) .addListener(invalidateLayout); } return $mdMedia.watchResponsiveAttributes( ['md-cols', 'md-row-height', 'md-gutter'], attrs, layoutIfMediaMatch); } function unwatchMedia() { ctrl.layoutDelegate = angular.noop; unwatchAttrs(); for (var mediaName in $mdConstant.MEDIA) { $mdMedia.getQuery($mdConstant.MEDIA[mediaName]) .removeListener(invalidateLayout); } } /** * Performs grid layout if the provided mediaName matches the currently * active media type. */ function layoutIfMediaMatch(mediaName) { if (mediaName == null) { // TODO(shyndman): It would be nice to only layout if we have // instances of attributes using this media type ctrl.invalidateLayout(); } else if ($mdMedia(mediaName)) { ctrl.invalidateLayout(); } } var lastLayoutProps; /** * Invokes the layout engine, and uses its results to lay out our * tile elements. * * @param {boolean} tilesInvalidated Whether tiles have been * added/removed/moved since the last layout. This is to avoid situations * where tiles are replaced with properties identical to their removed * counterparts. */ function layoutDelegate(tilesInvalidated) { var tiles = getTileElements(); var props = { tileSpans: getTileSpans(tiles), colCount: getColumnCount(), rowMode: getRowMode(), rowHeight: getRowHeight(), gutter: getGutter() }; if (!tilesInvalidated && angular.equals(props, lastLayoutProps)) { return; } var performance = $mdGridLayout(props.colCount, props.tileSpans, tiles) .map(function(tilePositions, rowCount) { return { grid: { element: element, style: getGridStyle(props.colCount, rowCount, props.gutter, props.rowMode, props.rowHeight) }, tiles: tilePositions.map(function(ps, i) { return { element: angular.element(tiles[i]), style: getTileStyle(ps.position, ps.spans, props.colCount, rowCount, props.gutter, props.rowMode, props.rowHeight) } }) } }) .reflow() .performance(); // Report layout scope.mdOnLayout({ $event: { performance: performance } }); lastLayoutProps = props; } // Use $interpolate to do some simple string interpolation as a convenience. var startSymbol = $interpolate.startSymbol(); var endSymbol = $interpolate.endSymbol(); // Returns an expression wrapped in the interpolator's start and end symbols. function expr(exprStr) { return startSymbol + exprStr + endSymbol; } // The amount of space a single 1x1 tile would take up (either width or height), used as // a basis for other calculations. This consists of taking the base size percent (as would be // if evenly dividing the size between cells), and then subtracting the size of one gutter. // However, since there are no gutters on the edges, each tile only uses a fration // (gutterShare = numGutters / numCells) of the gutter size. (Imagine having one gutter per // tile, and then breaking up the extra gutter on the edge evenly among the cells). var UNIT = $interpolate(expr('share') + '% - (' + expr('gutter') + ' * ' + expr('gutterShare') + ')'); // The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value. // The position comes the size of a 1x1 tile plus gutter for each previous tile in the // row/column (offset). var POSITION = $interpolate('calc((' + expr('unit') + ' + ' + expr('gutter') + ') * ' + expr('offset') + ')'); // The actual size of a tile, e.g., width or height, taking rowSpan or colSpan into account. // This is computed by multiplying the base unit by the rowSpan/colSpan, and then adding back // in the space that the gutter would normally have used (which was already accounted for in // the base unit calculation). var DIMENSION = $interpolate('calc((' + expr('unit') + ') * ' + expr('span') + ' + (' + expr('span') + ' - 1) * ' + expr('gutter') + ')'); /** * Gets the styles applied to a tile element described by the given parameters. * @param {{row: number, col: number}} position The row and column indices of the tile. * @param {{row: number, col: number}} spans The rowSpan and colSpan of the tile. * @param {number} colCount The number of columns. * @param {number} rowCount The number of rows. * @param {string} gutter The amount of space between tiles. This will be something like * '5px' or '2em'. * @param {string} rowMode The row height mode. Can be one of: * 'fixed': all rows have a fixed size, given by rowHeight, * 'ratio': row height defined as a ratio to width, or * 'fit': fit to the grid-list element height, divinding evenly among rows. * @param {string|number} rowHeight The height of a row. This is only used for 'fixed' mode and * for 'ratio' mode. For 'ratio' mode, this is the *ratio* of width-to-height (e.g., 0.75). * @returns {Object} Map of CSS properties to be applied to the style element. Will define * values for top, left, width, height, marginTop, and paddingTop. */ function getTileStyle(position, spans, colCount, rowCount, gutter, rowMode, rowHeight) { // TODO(shyndman): There are style caching opportunities here. // Percent of the available horizontal space that one column takes up. var hShare = (1 / colCount) * 100; // Fraction of the gutter size that each column takes up. var hGutterShare = (colCount - 1) / colCount; // Base horizontal size of a column. var hUnit = UNIT({share: hShare, gutterShare: hGutterShare, gutter: gutter}); // The width and horizontal position of each tile is always calculated the same way, but the // height and vertical position depends on the rowMode. var ltr = document.dir != 'rtl' && document.body.dir != 'rtl'; var style = ltr ? { left: POSITION({ unit: hUnit, offset: position.col, gutter: gutter }), width: DIMENSION({ unit: hUnit, span: spans.col, gutter: gutter }), // resets paddingTop: '', marginTop: '', top: '', height: '' } : { right: POSITION({ unit: hUnit, offset: position.col, gutter: gutter }), width: DIMENSION({ unit: hUnit, span: spans.col, gutter: gutter }), // resets paddingTop: '', marginTop: '', top: '', height: '' }; switch (rowMode) { case 'fixed': // In fixed mode, simply use the given rowHeight. style.top = POSITION({ unit: rowHeight, offset: position.row, gutter: gutter }); style.height = DIMENSION({ unit: rowHeight, span: spans.row, gutter: gutter }); break; case 'ratio': // Percent of the available vertical space that one row takes up. Here, rowHeight holds // the ratio value. For example, if the width:height ratio is 4:3, rowHeight = 1.333. var vShare = hShare / rowHeight; // Base veritcal size of a row. var vUnit = UNIT({ share: vShare, gutterShare: hGutterShare, gutter: gutter }); // padidngTop and marginTop are used to maintain the given aspect ratio, as // a percentage-based value for these properties is applied to the *width* of the // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties style.paddingTop = DIMENSION({ unit: vUnit, span: spans.row, gutter: gutter}); style.marginTop = POSITION({ unit: vUnit, offset: position.row, gutter: gutter }); break; case 'fit': // Fraction of the gutter size that each column takes up. var vGutterShare = (rowCount - 1) / rowCount; // Percent of the available vertical space that one row takes up. var vShare = (1 / rowCount) * 100; // Base vertical size of a row. var vUnit = UNIT({share: vShare, gutterShare: vGutterShare, gutter: gutter}); style.top = POSITION({unit: vUnit, offset: position.row, gutter: gutter}); style.height = DIMENSION({unit: vUnit, span: spans.row, gutter: gutter}); break; } return style; } function getGridStyle(colCount, rowCount, gutter, rowMode, rowHeight) { var style = {}; switch(rowMode) { case 'fixed': style.height = DIMENSION({ unit: rowHeight, span: rowCount, gutter: gutter }); style.paddingBottom = ''; break; case 'ratio': // rowHeight is width / height var hGutterShare = colCount === 1 ? 0 : (colCount - 1) / colCount, hShare = (1 / colCount) * 100, vShare = hShare * (1 / rowHeight), vUnit = UNIT({ share: vShare, gutterShare: hGutterShare, gutter: gutter }); style.height = ''; style.paddingBottom = DIMENSION({ unit: vUnit, span: rowCount, gutter: gutter}); break; case 'fit': // noop, as the height is user set break; } return style; } function getTileElements() { return [].filter.call(element.children(), function(ele) { return ele.tagName == 'MD-GRID-TILE' && !ele.$$mdDestroyed; }); } /** * Gets an array of objects containing the rowspan and colspan for each tile. * @returns {Array<{row: number, col: number}>} */ function getTileSpans(tileElements) { return [].map.call(tileElements, function(ele) { var ctrl = angular.element(ele).controller('mdGridTile'); return { row: parseInt( $mdMedia.getResponsiveAttribute(ctrl.$attrs, 'md-rowspan'), 10) || 1, col: parseInt( $mdMedia.getResponsiveAttribute(ctrl.$attrs, 'md-colspan'), 10) || 1 }; }); } function getColumnCount() { var colCount = parseInt($mdMedia.getResponsiveAttribute(attrs, 'md-cols'), 10); if (isNaN(colCount)) { throw 'md-grid-list: md-cols attribute was not found, or contained a non-numeric value'; } return colCount; } function getGutter() { return applyDefaultUnit($mdMedia.getResponsiveAttribute(attrs, 'md-gutter') || 1); } function getRowHeight() { var rowHeight = $mdMedia.getResponsiveAttribute(attrs, 'md-row-height'); if (!rowHeight) { throw 'md-grid-list: md-row-height attribute was not found'; } switch (getRowMode()) { case 'fixed': return applyDefaultUnit(rowHeight); case 'ratio': var whRatio = rowHeight.split(':'); return parseFloat(whRatio[0]) / parseFloat(whRatio[1]); case 'fit': return 0; // N/A } } function getRowMode() { var rowHeight = $mdMedia.getResponsiveAttribute(attrs, 'md-row-height'); if (!rowHeight) { throw 'md-grid-list: md-row-height attribute was not found'; } if (rowHeight == 'fit') { return 'fit'; } else if (rowHeight.indexOf(':') !== -1) { return 'ratio'; } else { return 'fixed'; } } function applyDefaultUnit(val) { return /\D$/.test(val) ? val : val + 'px'; } } } /* @ngInject */ function GridListController($mdUtil) { this.layoutInvalidated = false; this.tilesInvalidated = false; this.$timeout_ = $mdUtil.nextTick; this.layoutDelegate = angular.noop; } GridListController.prototype = { invalidateTiles: function() { this.tilesInvalidated = true; this.invalidateLayout(); }, invalidateLayout: function() { if (this.layoutInvalidated) { return; } this.layoutInvalidated = true; this.$timeout_(angular.bind(this, this.layout)); }, layout: function() { try { this.layoutDelegate(this.tilesInvalidated); } finally { this.layoutInvalidated = false; this.tilesInvalidated = false; } } }; /* @ngInject */ function GridLayoutFactory($mdUtil) { var defaultAnimator = GridTileAnimator; /** * Set the reflow animator callback */ GridLayout.animateWith = function(customAnimator) { defaultAnimator = !angular.isFunction(customAnimator) ? GridTileAnimator : customAnimator; }; return GridLayout; /** * Publish layout function */ function GridLayout(colCount, tileSpans) { var self, layoutInfo, gridStyles, layoutTime, mapTime, reflowTime; layoutTime = $mdUtil.time(function() { layoutInfo = calculateGridFor(colCount, tileSpans); }); return self = { /** * An array of objects describing each tile's position in the grid. */ layoutInfo: function() { return layoutInfo; }, /** * Maps grid positioning to an element and a set of styles using the * provided updateFn. */ map: function(updateFn) { mapTime = $mdUtil.time(function() { var info = self.layoutInfo(); gridStyles = updateFn(info.positioning, info.rowCount); }); return self; }, /** * Default animator simply sets the element.css( ). An alternate * animator can be provided as an argument. The function has the following * signature: * * function({grid: {element: JQLite, style: Object}, tiles: Array<{element: JQLite, style: Object}>) */ reflow: function(animatorFn) { reflowTime = $mdUtil.time(function() { var animator = animatorFn || defaultAnimator; animator(gridStyles.grid, gridStyles.tiles); }); return self; }, /** * Timing for the most recent layout run. */ performance: function() { return { tileCount: tileSpans.length, layoutTime: layoutTime, mapTime: mapTime, reflowTime: reflowTime, totalTime: layoutTime + mapTime + reflowTime }; } }; } /** * Default Gridlist animator simple sets the css for each element; * NOTE: any transitions effects must be manually set in the CSS. * e.g. * * md-grid-tile { * transition: all 700ms ease-out 50ms; * } * */ function GridTileAnimator(grid, tiles) { grid.element.css(grid.style); tiles.forEach(function(t) { t.element.css(t.style); }) } /** * Calculates the positions of tiles. * * The algorithm works as follows: * An Array with length colCount (spaceTracker) keeps track of * available tiling positions, where elements of value 0 represents an * empty position. Space for a tile is reserved by finding a sequence of * 0s with length <= than the tile's colspan. When such a space has been * found, the occupied tile positions are incremented by the tile's * rowspan value, as these positions have become unavailable for that * many rows. * * If the end of a row has been reached without finding space for the * tile, spaceTracker's elements are each decremented by 1 to a minimum * of 0. Rows are searched in this fashion until space is found. */ function calculateGridFor(colCount, tileSpans) { var curCol = 0, curRow = 0, spaceTracker = newSpaceTracker(); return { positioning: tileSpans.map(function(spans, i) { return { spans: spans, position: reserveSpace(spans, i) }; }), rowCount: curRow + Math.max.apply(Math, spaceTracker) }; function reserveSpace(spans, i) { if (spans.col > colCount) { throw 'md-grid-list: Tile at position ' + i + ' has a colspan ' + '(' + spans.col + ') that exceeds the column count ' + '(' + colCount + ')'; } var start = 0, end = 0; // TODO(shyndman): This loop isn't strictly necessary if you can // determine the minimum number of rows before a space opens up. To do // this, recognize that you've iterated across an entire row looking for // space, and if so fast-forward by the minimum rowSpan count. Repeat // until the required space opens up. while (end - start < spans.col) { if (curCol >= colCount) { nextRow(); continue; } start = spaceTracker.indexOf(0, curCol); if (start === -1 || (end = findEnd(start + 1)) === -1) { start = end = 0; nextRow(); continue; } curCol = end + 1; } adjustRow(start, spans.col, spans.row); curCol = start + spans.col; return { col: start, row: curRow }; } function nextRow() { curCol = 0; curRow++; adjustRow(0, colCount, -1); // Decrement row spans by one } function adjustRow(from, cols, by) { for (var i = from; i < from + cols; i++) { spaceTracker[i] = Math.max(spaceTracker[i] + by, 0); } } function findEnd(start) { var i; for (i = start; i < spaceTracker.length; i++) { if (spaceTracker[i] !== 0) { return i; } } if (i === spaceTracker.length) { return i; } } function newSpaceTracker() { var tracker = []; for (var i = 0; i < colCount; i++) { tracker.push(0); } return tracker; } } } /** * @ngdoc directive * @name mdGridTile * @module material.components.gridList * @restrict E * @description * Tiles contain the content of an `md-grid-list`. They span one or more grid * cells vertically or horizontally, and use `md-grid-tile-{footer,header}` to * display secondary content. * * ### Responsive Attributes * * The `md-grid-tile` directive supports "responsive" attributes, which allow * different `md-rowspan` and `md-colspan` values depending on the currently * matching media query. * * In order to set a responsive attribute, first define the fallback value with * the standard attribute name, then add additional attributes with the * following convention: `{base-attribute-name}-{media-query-name}="{value}"` * (ie. `md-colspan-sm="4"`) * * @param {number=} md-colspan The number of columns to span (default 1). Cannot * exceed the number of columns in the grid. Supports interpolation. * @param {number=} md-rowspan The number of rows to span (default 1). Supports * interpolation. * * @usage * With header: * * * *

This is a header

*
*
*
* * With footer: * * * *

This is a footer

*
*
*
* * Spanning multiple rows/columns: * * * * * * Responsive attributes: * * * * */ function GridTileDirective($mdMedia) { return { restrict: 'E', require: '^mdGridList', template: '
', transclude: true, scope: {}, // Simple controller that exposes attributes to the grid directive controller: ["$attrs", function($attrs) { this.$attrs = $attrs; }], link: postLink }; function postLink(scope, element, attrs, gridCtrl) { // Apply semantics element.attr('role', 'listitem'); // If our colspan or rowspan changes, trigger a layout var unwatchAttrs = $mdMedia.watchResponsiveAttributes(['md-colspan', 'md-rowspan'], attrs, angular.bind(gridCtrl, gridCtrl.invalidateLayout)); // Tile registration/deregistration gridCtrl.invalidateTiles(); scope.$on('$destroy', function() { // Mark the tile as destroyed so it is no longer considered in layout, // even if the DOM element sticks around (like during a leave animation) element[0].$$mdDestroyed = true; unwatchAttrs(); gridCtrl.invalidateLayout(); }); if (angular.isDefined(scope.$parent.$index)) { scope.$watch(function() { return scope.$parent.$index; }, function indexChanged(newIdx, oldIdx) { if (newIdx === oldIdx) { return; } gridCtrl.invalidateTiles(); }); } } } function GridTileCaptionDirective() { return { template: '
', transclude: true }; } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.input */ mdInputContainerDirective.$inject = ["$mdTheming", "$parse"]; inputTextareaDirective.$inject = ["$mdUtil", "$window", "$mdAria", "$timeout", "$mdGesture"]; mdMaxlengthDirective.$inject = ["$animate", "$mdUtil"]; placeholderDirective.$inject = ["$compile"]; ngMessageDirective.$inject = ["$mdUtil"]; mdSelectOnFocusDirective.$inject = ["$timeout"]; mdInputInvalidMessagesAnimation.$inject = ["$$AnimateRunner", "$animateCss", "$mdUtil", "$log"]; ngMessagesAnimation.$inject = ["$$AnimateRunner", "$animateCss", "$mdUtil", "$log"]; ngMessageAnimation.$inject = ["$$AnimateRunner", "$animateCss", "$mdUtil", "$log"]; var inputModule = angular.module('material.components.input', [ 'material.core' ]) .directive('mdInputContainer', mdInputContainerDirective) .directive('label', labelDirective) .directive('input', inputTextareaDirective) .directive('textarea', inputTextareaDirective) .directive('mdMaxlength', mdMaxlengthDirective) .directive('placeholder', placeholderDirective) .directive('ngMessages', ngMessagesDirective) .directive('ngMessage', ngMessageDirective) .directive('ngMessageExp', ngMessageDirective) .directive('mdSelectOnFocus', mdSelectOnFocusDirective) .animation('.md-input-invalid', mdInputInvalidMessagesAnimation) .animation('.md-input-messages-animation', ngMessagesAnimation) .animation('.md-input-message-animation', ngMessageAnimation); // If we are running inside of tests; expose some extra services so that we can test them if (window._mdMocksIncluded) { inputModule.service('$$mdInput', function() { return { // special accessor to internals... useful for testing messages: { show : showInputMessages, hide : hideInputMessages, getElement : getMessagesElement } } }) // Register a service for each animation so that we can easily inject them into unit tests .service('mdInputInvalidAnimation', mdInputInvalidMessagesAnimation) .service('mdInputMessagesAnimation', ngMessagesAnimation) .service('mdInputMessageAnimation', ngMessageAnimation); } /** * @ngdoc directive * @name mdInputContainer * @module material.components.input * * @restrict E * * @description * `` is the parent of any input or textarea element. * * Input and textarea elements will not behave properly unless the md-input-container * parent is provided. * * A single `` should contain only one `` element, otherwise it will throw an error. * * Exception: Hidden inputs (``) are ignored and will not throw an error, so * you may combine these with other inputs. * * Note: When using `ngMessages` with your input element, make sure the message and container elements * are *block* elements, otherwise animations applied to the messages will not look as intended. Either use a `div` and * apply the `ng-message` and `ng-messages` classes respectively, or use the `md-block` class on your element. * * @param md-is-error {expression=} When the given expression evaluates to true, the input container * will go into error state. Defaults to erroring if the input has been touched and is invalid. * @param md-no-float {boolean=} When present, `placeholder` attributes on the input will not be converted to floating * labels. * * @usage * * * * * * * * * * * * * * *

When disabling floating labels

* * * * * * * */ function mdInputContainerDirective($mdTheming, $parse) { ContainerCtrl.$inject = ["$scope", "$element", "$attrs", "$animate"]; var INPUT_TAGS = ['INPUT', 'TEXTAREA', 'SELECT', 'MD-SELECT']; var LEFT_SELECTORS = INPUT_TAGS.reduce(function(selectors, isel) { return selectors.concat(['md-icon ~ ' + isel, '.md-icon ~ ' + isel]); }, []).join(","); var RIGHT_SELECTORS = INPUT_TAGS.reduce(function(selectors, isel) { return selectors.concat([isel + ' ~ md-icon', isel + ' ~ .md-icon']); }, []).join(","); return { restrict: 'E', compile: compile, controller: ContainerCtrl }; function compile(tElement) { // Check for both a left & right icon var leftIcon = tElement[0].querySelector(LEFT_SELECTORS); var rightIcon = tElement[0].querySelector(RIGHT_SELECTORS); if (leftIcon) { tElement.addClass('md-icon-left'); } if (rightIcon) { tElement.addClass('md-icon-right'); } return function postLink(scope, element) { $mdTheming(element); }; } function ContainerCtrl($scope, $element, $attrs, $animate) { var self = this; self.isErrorGetter = $attrs.mdIsError && $parse($attrs.mdIsError); self.delegateClick = function() { self.input.focus(); }; self.element = $element; self.setFocused = function(isFocused) { $element.toggleClass('md-input-focused', !!isFocused); }; self.setHasValue = function(hasValue) { $element.toggleClass('md-input-has-value', !!hasValue); }; self.setHasPlaceholder = function(hasPlaceholder) { $element.toggleClass('md-input-has-placeholder', !!hasPlaceholder); }; self.setInvalid = function(isInvalid) { if (isInvalid) { $animate.addClass($element, 'md-input-invalid'); } else { $animate.removeClass($element, 'md-input-invalid'); } }; $scope.$watch(function() { return self.label && self.input; }, function(hasLabelAndInput) { if (hasLabelAndInput && !self.label.attr('for')) { self.label.attr('for', self.input.attr('id')); } }); } } function labelDirective() { return { restrict: 'E', require: '^?mdInputContainer', link: function(scope, element, attr, containerCtrl) { if (!containerCtrl || attr.mdNoFloat || element.hasClass('md-container-ignore')) return; containerCtrl.label = element; scope.$on('$destroy', function() { containerCtrl.label = null; }); } }; } /** * @ngdoc directive * @name mdInput * @restrict E * @module material.components.input * * @description * You can use any `` or ` *
*
This is required!
*
That's too long!
*
*
* * * * * * * * * *

Notes

* * - Requires [ngMessages](https://docs.angularjs.org/api/ngMessages). * - Behaves like the [AngularJS input directive](https://docs.angularjs.org/api/ng/directive/input). * * The `md-input` and `md-input-container` directives use very specific positioning to achieve the * error animation effects. Therefore, it is *not* advised to use the Layout system inside of the * `` tags. Instead, use relative or absolute positioning. * * *

Textarea directive

* The `textarea` element within a `md-input-container` has the following specific behavior: * - By default the `textarea` grows as the user types. This can be disabled via the `md-no-autogrow` * attribute. * - If a `textarea` has the `rows` attribute, it will treat the `rows` as the minimum height and will * continue growing as the user types. For example a textarea with `rows="3"` will be 3 lines of text * high initially. If no rows are specified, the directive defaults to 1. * - The textarea's height gets set on initialization, as well as while the user is typing. In certain situations * (e.g. while animating) the directive might have been initialized, before the element got it's final height. In * those cases, you can trigger a resize manually by broadcasting a `md-resize-textarea` event on the scope. * - If you want a `textarea` to stop growing at a certain point, you can specify the `max-rows` attribute. * - The textarea's bottom border acts as a handle which users can drag, in order to resize the element vertically. * Once the user has resized a `textarea`, the autogrowing functionality becomes disabled. If you don't want a * `textarea` to be resizeable by the user, you can add the `md-no-resize` attribute. */ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout, $mdGesture) { return { restrict: 'E', require: ['^?mdInputContainer', '?ngModel', '?^form'], link: postLink }; function postLink(scope, element, attr, ctrls) { var containerCtrl = ctrls[0]; var hasNgModel = !!ctrls[1]; var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); var parentForm = ctrls[2]; var isReadonly = angular.isDefined(attr.readonly); var mdNoAsterisk = $mdUtil.parseAttributeBoolean(attr.mdNoAsterisk); var tagName = element[0].tagName.toLowerCase(); if (!containerCtrl) return; if (attr.type === 'hidden') { element.attr('aria-hidden', 'true'); return; } else if (containerCtrl.input) { if (containerCtrl.input[0].contains(element[0])) { return; } else { throw new Error(" can only have *one* , * * * */ function mdSelectOnFocusDirective($timeout) { return { restrict: 'A', link: postLink }; function postLink(scope, element, attr) { if (element[0].nodeName !== 'INPUT' && element[0].nodeName !== "TEXTAREA") return; var preventMouseUp = false; element .on('focus', onFocus) .on('mouseup', onMouseUp); scope.$on('$destroy', function() { element .off('focus', onFocus) .off('mouseup', onMouseUp); }); function onFocus() { preventMouseUp = true; $timeout(function() { // Use HTMLInputElement#select to fix firefox select issues. // The debounce is here for Edge's sake, otherwise the selection doesn't work. element[0].select(); // This should be reset from inside the `focus`, because the event might // have originated from something different than a click, e.g. a keyboard event. preventMouseUp = false; }, 1, false); } // Prevents the default action of the first `mouseup` after a focus. // This is necessary, because browsers fire a `mouseup` right after the element // has been focused. In some browsers (Firefox in particular) this can clear the // selection. There are examples of the problem in issue #7487. function onMouseUp(event) { if (preventMouseUp) { event.preventDefault(); } } } } var visibilityDirectives = ['ngIf', 'ngShow', 'ngHide', 'ngSwitchWhen', 'ngSwitchDefault']; function ngMessagesDirective() { return { restrict: 'EA', link: postLink, // This is optional because we don't want target *all* ngMessage instances, just those inside of // mdInputContainer. require: '^^?mdInputContainer' }; function postLink(scope, element, attrs, inputContainer) { // If we are not a child of an input container, don't do anything if (!inputContainer) return; // Add our animation class element.toggleClass('md-input-messages-animation', true); // Add our md-auto-hide class to automatically hide/show messages when container is invalid element.toggleClass('md-auto-hide', true); // If we see some known visibility directives, remove the md-auto-hide class if (attrs.mdAutoHide == 'false' || hasVisibiltyDirective(attrs)) { element.toggleClass('md-auto-hide', false); } } function hasVisibiltyDirective(attrs) { return visibilityDirectives.some(function(attr) { return attrs[attr]; }); } } function ngMessageDirective($mdUtil) { return { restrict: 'EA', compile: compile, priority: 100 }; function compile(tElement) { if (!isInsideInputContainer(tElement)) { // When the current element is inside of a document fragment, then we need to check for an input-container // in the postLink, because the element will be later added to the DOM and is currently just in a temporary // fragment, which causes the input-container check to fail. if (isInsideFragment()) { return function (scope, element) { if (isInsideInputContainer(element)) { // Inside of the postLink function, a ngMessage directive will be a comment element, because it's // currently hidden. To access the shown element, we need to use the element from the compile function. initMessageElement(tElement); } }; } } else { initMessageElement(tElement); } function isInsideFragment() { var nextNode = tElement[0]; while (nextNode = nextNode.parentNode) { if (nextNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { return true; } } return false; } function isInsideInputContainer(element) { return !!$mdUtil.getClosest(element, "md-input-container"); } function initMessageElement(element) { // Add our animation class element.toggleClass('md-input-message-animation', true); } } } var $$AnimateRunner, $animateCss, $mdUtil, $log; function mdInputInvalidMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log); return { addClass: function(element, className, done) { showInputMessages(element, done); } // NOTE: We do not need the removeClass method, because the message ng-leave animation will fire }; } function ngMessagesAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log); return { enter: function(element, done) { showInputMessages(element, done); }, leave: function(element, done) { hideInputMessages(element, done); }, addClass: function(element, className, done) { if (className == "ng-hide") { hideInputMessages(element, done); } else { done(); } }, removeClass: function(element, className, done) { if (className == "ng-hide") { showInputMessages(element, done); } else { done(); } } }; } function ngMessageAnimation($$AnimateRunner, $animateCss, $mdUtil, $log) { saveSharedServices($$AnimateRunner, $animateCss, $mdUtil, $log); return { enter: function(element, done) { var animator = showMessage(element); animator.start().done(done); }, leave: function(element, done) { var animator = hideMessage(element); animator.start().done(done); } }; } function showInputMessages(element, done) { var animators = [], animator; var messages = getMessagesElement(element); var children = messages.children(); if (messages.length == 0 || children.length == 0) { $log.warn('mdInput messages show animation called on invalid messages element: ', element); done(); return; } angular.forEach(children, function(child) { animator = showMessage(angular.element(child)); animators.push(animator.start()); }); $$AnimateRunner.all(animators, done); } function hideInputMessages(element, done) { var animators = [], animator; var messages = getMessagesElement(element); var children = messages.children(); if (messages.length == 0 || children.length == 0) { $log.warn('mdInput messages hide animation called on invalid messages element: ', element); done(); return; } angular.forEach(children, function(child) { animator = hideMessage(angular.element(child)); animators.push(animator.start()); }); $$AnimateRunner.all(animators, done); } function showMessage(element) { var height = parseInt(window.getComputedStyle(element[0]).height); var topMargin = parseInt(window.getComputedStyle(element[0]).marginTop); var messages = getMessagesElement(element); var container = getInputElement(element); // Check to see if the message is already visible so we can skip var alreadyVisible = (topMargin > -height); // If we have the md-auto-hide class, the md-input-invalid animation will fire, so we can skip if (alreadyVisible || (messages.hasClass('md-auto-hide') && !container.hasClass('md-input-invalid'))) { return $animateCss(element, {}); } return $animateCss(element, { event: 'enter', structural: true, from: {"opacity": 0, "margin-top": -height + "px"}, to: {"opacity": 1, "margin-top": "0"}, duration: 0.3 }); } function hideMessage(element) { var height = element[0].offsetHeight; var styles = window.getComputedStyle(element[0]); // If we are already hidden, just return an empty animation if (parseInt(styles.opacity) === 0) { return $animateCss(element, {}); } // Otherwise, animate return $animateCss(element, { event: 'leave', structural: true, from: {"opacity": 1, "margin-top": 0}, to: {"opacity": 0, "margin-top": -height + "px"}, duration: 0.3 }); } function getInputElement(element) { var inputContainer = element.controller('mdInputContainer'); return inputContainer.element; } function getMessagesElement(element) { // If we ARE the messages element, just return ourself if (element.hasClass('md-input-messages-animation')) { return element; } // If we are a ng-message element, we need to traverse up the DOM tree if (element.hasClass('md-input-message-animation')) { return angular.element($mdUtil.getClosest(element, function(node) { return node.classList.contains('md-input-messages-animation'); })); } // Otherwise, we can traverse down return angular.element(element[0].querySelector('.md-input-messages-animation')); } function saveSharedServices(_$$AnimateRunner_, _$animateCss_, _$mdUtil_, _$log_) { $$AnimateRunner = _$$AnimateRunner_; $animateCss = _$animateCss_; $mdUtil = _$mdUtil_; $log = _$log_; } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.list * @description * List module */ MdListController.$inject = ["$scope", "$element", "$mdListInkRipple"]; mdListDirective.$inject = ["$mdTheming"]; mdListItemDirective.$inject = ["$mdAria", "$mdConstant", "$mdUtil", "$timeout"]; angular.module('material.components.list', [ 'material.core' ]) .controller('MdListController', MdListController) .directive('mdList', mdListDirective) .directive('mdListItem', mdListItemDirective); /** * @ngdoc directive * @name mdList * @module material.components.list * * @restrict E * * @description * The `` directive is a list container for 1..n `` tags. * * @usage * * * * *
*

{{item.title}}

*

{{item.description}}

*
*
*
*
*/ function mdListDirective($mdTheming) { return { restrict: 'E', compile: function(tEl) { tEl[0].setAttribute('role', 'list'); return $mdTheming; } }; } /** * @ngdoc directive * @name mdListItem * @module material.components.list * * @restrict E * * @description * A `md-list-item` element can be used to represent some information in a row.
* * @usage * ### Single Row Item * * * Single Row Item * * * * ### Multiple Lines * By using the following markup, you will be able to have two lines inside of one `md-list-item`. * * * *
*

First Line

*

Second Line

*
*
*
* * It is also possible to have three lines inside of one list item. * * * *
*

First Line

*

Second Line

*

Third Line

*
*
*
* * ### Secondary Items * Secondary items are elements which will be aligned at the end of the `md-list-item`. * * * * Single Row Item * * Secondary Button * * * * * It also possible to have multiple secondary items inside of one `md-list-item`. * * * * Single Row Item * First Button * Second Button * * * * ### Proxy Item * Proxies are elements, which will execute their specific action on click
* Currently supported proxy items are * - `md-checkbox` (Toggle) * - `md-switch` (Toggle) * - `md-menu` (Open) * * This means, when using a supported proxy item inside of `md-list-item`, the list item will * automatically become clickable and executes the associated action of the proxy element on click. * * It is possible to disable this behavior by applying the `md-no-proxy` class to the list item. * * * * No Proxy List * * * * * Here are a few examples of proxy elements inside of a list item. * * * * First Line * * * * * The `md-checkbox` element will be automatically detected as a proxy element and will toggle on click. * * * * First Line * * * * * The recognized `md-switch` will toggle its state, when the user clicks on the `md-list-item`. * * It is also possible to have a `md-menu` inside of a `md-list-item`. * * *

Click anywhere to fire the secondary action

* * * * * * * * Redial * * * * * Check voicemail * * * * * * Notifications * * * * *
*
* * The menu will automatically open, when the users clicks on the `md-list-item`.
* * If the developer didn't specify any position mode on the menu, the `md-list-item` will automatically detect the * position mode and applies it to the `md-menu`. * * ### Avatars * Sometimes you may want to have some avatars inside of the `md-list-item `.
* You are able to create a optimized icon for the list item, by applying the `.md-avatar` class on the `` element. * * * * * Alan Turing * * * When using `` for an avatar, you have to use the `.md-avatar-icon` class. * * * * Timothy Kopra * * * * In cases, you have a `md-list-item`, which doesn't have any avatar, * but you want to align it with the other avatar items, you have to use the `.md-offset` class. * * * * Jon Doe * * * * ### DOM modification * The `md-list-item` component automatically detects if the list item should be clickable. * * --- * If the `md-list-item` is clickable, we wrap all content inside of a `
` and create * an overlaying button, which will will execute the given actions (like `ng-href`, `ng-click`) * * We create an overlaying button, instead of wrapping all content inside of the button, * because otherwise some elements may not be clickable inside of the button. * * --- * When using a secondary item inside of your list item, the `md-list-item` component will automatically create * a secondary container at the end of the `md-list-item`, which contains all secondary items. * * The secondary item container is not static, because otherwise the overflow will not work properly on the * list item. * */ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { var proxiedTypes = ['md-checkbox', 'md-switch', 'md-menu']; return { restrict: 'E', controller: 'MdListController', compile: function(tEl, tAttrs) { // Check for proxy controls (no ng-click on parent, and a control inside) var secondaryItems = tEl[0].querySelectorAll('.md-secondary'); var hasProxiedElement; var proxyElement; var itemContainer = tEl; tEl[0].setAttribute('role', 'listitem'); if (tAttrs.ngClick || tAttrs.ngDblclick || tAttrs.ngHref || tAttrs.href || tAttrs.uiSref || tAttrs.ngAttrUiSref) { wrapIn('button'); } else if (!tEl.hasClass('md-no-proxy')) { for (var i = 0, type; type = proxiedTypes[i]; ++i) { if (proxyElement = tEl[0].querySelector(type)) { hasProxiedElement = true; break; } } if (hasProxiedElement) { wrapIn('div'); } else { tEl.addClass('md-no-proxy'); } } wrapSecondaryItems(); setupToggleAria(); if (hasProxiedElement && proxyElement.nodeName === "MD-MENU") { setupProxiedMenu(); } function setupToggleAria() { var toggleTypes = ['md-switch', 'md-checkbox']; var toggle; for (var i = 0, toggleType; toggleType = toggleTypes[i]; ++i) { if (toggle = tEl.find(toggleType)[0]) { if (!toggle.hasAttribute('aria-label')) { var p = tEl.find('p')[0]; if (!p) return; toggle.setAttribute('aria-label', 'Toggle ' + p.textContent); } } } } function setupProxiedMenu() { var menuEl = angular.element(proxyElement); var isEndAligned = menuEl.parent().hasClass('md-secondary-container') || proxyElement.parentNode.firstElementChild !== proxyElement; var xAxisPosition = 'left'; if (isEndAligned) { // When the proxy item is aligned at the end of the list, we have to set the origin to the end. xAxisPosition = 'right'; } // Set the position mode / origin of the proxied menu. if (!menuEl.attr('md-position-mode')) { menuEl.attr('md-position-mode', xAxisPosition + ' target'); } // Apply menu open binding to menu button var menuOpenButton = menuEl.children().eq(0); if (!hasClickEvent(menuOpenButton[0])) { menuOpenButton.attr('ng-click', '$mdMenu.open($event)'); } if (!menuOpenButton.attr('aria-label')) { menuOpenButton.attr('aria-label', 'Open List Menu'); } } function wrapIn(type) { if (type == 'div') { itemContainer = angular.element('
'); itemContainer.append(tEl.contents()); tEl.addClass('md-proxy-focus'); } else { // Element which holds the default list-item content. itemContainer = angular.element( '
'+ '
'+ '
' ); // Button which shows ripple and executes primary action. var buttonWrap = angular.element( '' ); copyAttributes(tEl[0], buttonWrap[0]); // If there is no aria-label set on the button (previously copied over if present) // we determine the label from the content and copy it to the button. if (!buttonWrap.attr('aria-label')) { buttonWrap.attr('aria-label', $mdAria.getText(tEl)); } // We allow developers to specify the `md-no-focus` class, to disable the focus style // on the button executor. Once more classes should be forwarded, we should probably make the // class forward more generic. if (tEl.hasClass('md-no-focus')) { buttonWrap.addClass('md-no-focus'); } // Append the button wrap before our list-item content, because it will overlay in relative. itemContainer.prepend(buttonWrap); itemContainer.children().eq(1).append(tEl.contents()); tEl.addClass('_md-button-wrap'); } tEl[0].setAttribute('tabindex', '-1'); tEl.append(itemContainer); } function wrapSecondaryItems() { var secondaryItemsWrapper = angular.element('
'); angular.forEach(secondaryItems, function(secondaryItem) { wrapSecondaryItem(secondaryItem, secondaryItemsWrapper); }); itemContainer.append(secondaryItemsWrapper); } function wrapSecondaryItem(secondaryItem, container) { // If the current secondary item is not a button, but contains a ng-click attribute, // the secondary item will be automatically wrapped inside of a button. if (secondaryItem && !isButton(secondaryItem) && secondaryItem.hasAttribute('ng-click')) { $mdAria.expect(secondaryItem, 'aria-label'); var buttonWrapper = angular.element(''); // Copy the attributes from the secondary item to the generated button. // We also support some additional attributes from the secondary item, // because some developers may use a ngIf, ngHide, ngShow on their item. copyAttributes(secondaryItem, buttonWrapper[0], ['ng-if', 'ng-hide', 'ng-show']); secondaryItem.setAttribute('tabindex', '-1'); buttonWrapper.append(secondaryItem); secondaryItem = buttonWrapper[0]; } if (secondaryItem && (!hasClickEvent(secondaryItem) || (!tAttrs.ngClick && isProxiedElement(secondaryItem)))) { // In this case we remove the secondary class, so we can identify it later, when we searching for the // proxy items. angular.element(secondaryItem).removeClass('md-secondary'); } tEl.addClass('md-with-secondary'); container.append(secondaryItem); } /** * Copies attributes from a source element to the destination element * By default the function will copy the most necessary attributes, supported * by the button executor for clickable list items. * @param source Element with the specified attributes * @param destination Element which will retrieve the attributes * @param extraAttrs Additional attributes, which will be copied over. */ function copyAttributes(source, destination, extraAttrs) { var copiedAttrs = $mdUtil.prefixer([ 'ng-if', 'ng-click', 'ng-dblclick', 'aria-label', 'ng-disabled', 'ui-sref', 'href', 'ng-href', 'rel', 'target', 'ng-attr-ui-sref', 'ui-sref-opts' ]); if (extraAttrs) { copiedAttrs = copiedAttrs.concat($mdUtil.prefixer(extraAttrs)); } angular.forEach(copiedAttrs, function(attr) { if (source.hasAttribute(attr)) { destination.setAttribute(attr, source.getAttribute(attr)); source.removeAttribute(attr); } }); } function isProxiedElement(el) { return proxiedTypes.indexOf(el.nodeName.toLowerCase()) != -1; } function isButton(el) { var nodeName = el.nodeName.toUpperCase(); return nodeName == "MD-BUTTON" || nodeName == "BUTTON"; } function hasClickEvent (element) { var attr = element.attributes; for (var i = 0; i < attr.length; i++) { if (tAttrs.$normalize(attr[i].name) === 'ngClick') return true; } return false; } return postLink; function postLink($scope, $element, $attr, ctrl) { $element.addClass('_md'); // private md component indicator for styling var proxies = [], firstElement = $element[0].firstElementChild, isButtonWrap = $element.hasClass('_md-button-wrap'), clickChild = isButtonWrap ? firstElement.firstElementChild : firstElement, hasClick = clickChild && hasClickEvent(clickChild), noProxies = $element.hasClass('md-no-proxy'); computeProxies(); computeClickable(); if (proxies.length) { angular.forEach(proxies, function(proxy) { proxy = angular.element(proxy); $scope.mouseActive = false; proxy.on('mousedown', function() { $scope.mouseActive = true; $timeout(function(){ $scope.mouseActive = false; }, 100); }) .on('focus', function() { if ($scope.mouseActive === false) { $element.addClass('md-focused'); } proxy.on('blur', function proxyOnBlur() { $element.removeClass('md-focused'); proxy.off('blur', proxyOnBlur); }); }); }); } function computeProxies() { if (firstElement && firstElement.children && !hasClick && !noProxies) { angular.forEach(proxiedTypes, function(type) { // All elements which are not capable for being used a proxy have the .md-secondary class // applied. These items had been sorted out in the secondary wrap function. angular.forEach(firstElement.querySelectorAll(type + ':not(.md-secondary)'), function(child) { proxies.push(child); }); }); } } function computeClickable() { if (proxies.length == 1 || hasClick) { $element.addClass('md-clickable'); if (!hasClick) { ctrl.attachRipple($scope, angular.element($element[0].querySelector('.md-no-style'))); } } } function isEventFromControl(event) { var forbiddenControls = ['md-slider']; // If there is no path property in the event, then we can assume that the event was not bubbled. if (!event.path) { return forbiddenControls.indexOf(event.target.tagName.toLowerCase()) !== -1; } // We iterate the event path up and check for a possible component. // Our maximum index to search, is the list item root. var maxPath = event.path.indexOf($element.children()[0]); for (var i = 0; i < maxPath; i++) { if (forbiddenControls.indexOf(event.path[i].tagName.toLowerCase()) !== -1) { return true; } } } var clickChildKeypressListener = function(e) { if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA' && !e.target.isContentEditable) { var keyCode = e.which || e.keyCode; if (keyCode == $mdConstant.KEY_CODE.SPACE) { if (clickChild) { clickChild.click(); e.preventDefault(); e.stopPropagation(); } } } }; if (!hasClick && !proxies.length) { clickChild && clickChild.addEventListener('keypress', clickChildKeypressListener); } $element.off('click'); $element.off('keypress'); if (proxies.length == 1 && clickChild) { $element.children().eq(0).on('click', function(e) { // When the event is coming from an control and it should not trigger the proxied element // then we are skipping. if (isEventFromControl(e)) return; var parentButton = $mdUtil.getClosest(e.target, 'BUTTON'); if (!parentButton && clickChild.contains(e.target)) { angular.forEach(proxies, function(proxy) { if (e.target !== proxy && !proxy.contains(e.target)) { if (proxy.nodeName === 'MD-MENU') { proxy = proxy.children[0]; } angular.element(proxy).triggerHandler('click'); } }); } }); } $scope.$on('$destroy', function () { clickChild && clickChild.removeEventListener('keypress', clickChildKeypressListener); }); } } }; } /* * @private * @ngdoc controller * @name MdListController * @module material.components.list * */ function MdListController($scope, $element, $mdListInkRipple) { var ctrl = this; ctrl.attachRipple = attachRipple; function attachRipple (scope, element) { var options = {}; $mdListInkRipple.attach(scope, element, options); } } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.menu */ angular.module('material.components.menu', [ 'material.core', 'material.components.backdrop' ]); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.menuBar */ angular.module('material.components.menuBar', [ 'material.core', 'material.components.icon', 'material.components.menu' ]); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.navBar */ MdNavBarController.$inject = ["$element", "$scope", "$timeout", "$mdConstant"]; MdNavItem.$inject = ["$mdAria", "$$rAF"]; MdNavItemController.$inject = ["$element"]; MdNavBar.$inject = ["$mdAria", "$mdTheming"]; angular.module('material.components.navBar', ['material.core']) .controller('MdNavBarController', MdNavBarController) .directive('mdNavBar', MdNavBar) .controller('MdNavItemController', MdNavItemController) .directive('mdNavItem', MdNavItem); /***************************************************************************** * PUBLIC DOCUMENTATION * *****************************************************************************/ /** * @ngdoc directive * @name mdNavBar * @module material.components.navBar * * @restrict E * * @description * The `` directive renders a list of material tabs that can be used * for top-level page navigation. Unlike ``, it has no concept of a tab * body and no bar pagination. * * Because it deals with page navigation, certain routing concepts are built-in. * Route changes via via ng-href, ui-sref, or ng-click events are supported. * Alternatively, the user could simply watch currentNavItem for changes. * * Accessibility functionality is implemented as a site navigator with a * listbox, according to * https://www.w3.org/TR/wai-aria-practices/#Site_Navigator_Tabbed_Style * * @param {string=} mdSelectedNavItem The name of the current tab; this must * match the name attribute of `` * @param {boolean=} mdNoInkBar If set to true, the ink bar will be hidden. * @param {string=} navBarAriaLabel An aria-label for the nav-bar * * @usage * * * * Page One * * Page Two * Page Three * * Page Four * * * * * (function() { * 'use strict'; * * $rootScope.$on('$routeChangeSuccess', function(event, current) { * $scope.currentLink = getCurrentLinkFromRoute(current); * }); * }); * */ /***************************************************************************** * mdNavItem *****************************************************************************/ /** * @ngdoc directive * @name mdNavItem * @module material.components.navBar * * @restrict E * * @description * `` describes a page navigation link within the `` * component. It renders an md-button as the actual link. * * Exactly one of the mdNavClick, mdNavHref, mdNavSref attributes are required * to be specified. * * @param {Function=} mdNavClick Function which will be called when the * link is clicked to change the page. Renders as an `ng-click`. * @param {string=} mdNavHref url to transition to when this link is clicked. * Renders as an `ng-href`. * @param {string=} mdNavSref Ui-router state to transition to when this link is * clicked. Renders as a `ui-sref`. * @param {!Object=} srefOpts Ui-router options that are passed to the * `$state.go()` function. See the [Ui-router documentation for details] * (https://ui-router.github.io/docs/latest/interfaces/transition.transitionoptions.html). * @param {string=} name The name of this link. Used by the nav bar to know * which link is currently selected. * @param {string=} aria-label Adds alternative text for accessibility * * @usage * See `` for usage. */ /***************************************************************************** * IMPLEMENTATION * *****************************************************************************/ function MdNavBar($mdAria, $mdTheming) { return { restrict: 'E', transclude: true, controller: MdNavBarController, controllerAs: 'ctrl', bindToController: true, scope: { 'mdSelectedNavItem': '=?', 'mdNoInkBar': '=?', 'navBarAriaLabel': '@?', }, template: '
' + '' + '' + '
', link: function(scope, element, attrs, ctrl) { $mdTheming(element); if (!ctrl.navBarAriaLabel) { $mdAria.expectAsync(element, 'aria-label', angular.noop); } }, }; } /** * Controller for the nav-bar component. * * Accessibility functionality is implemented as a site navigator with a * listbox, according to * https://www.w3.org/TR/wai-aria-practices/#Site_Navigator_Tabbed_Style * @param {!angular.JQLite} $element * @param {!angular.Scope} $scope * @param {!angular.Timeout} $timeout * @param {!Object} $mdConstant * @constructor * @final * @ngInject */ function MdNavBarController($element, $scope, $timeout, $mdConstant) { // Injected variables /** @private @const {!angular.Timeout} */ this._$timeout = $timeout; /** @private @const {!angular.Scope} */ this._$scope = $scope; /** @private @const {!Object} */ this._$mdConstant = $mdConstant; // Data-bound variables. /** @type {string} */ this.mdSelectedNavItem; /** @type {string} */ this.navBarAriaLabel; // State variables. /** @type {?angular.JQLite} */ this._navBarEl = $element[0]; /** @type {?angular.JQLite} */ this._inkbar; var self = this; // need to wait for transcluded content to be available var deregisterTabWatch = this._$scope.$watch(function() { return self._navBarEl.querySelectorAll('._md-nav-button').length; }, function(newLength) { if (newLength > 0) { self._initTabs(); deregisterTabWatch(); } }); } /** * Initializes the tab components once they exist. * @private */ MdNavBarController.prototype._initTabs = function() { this._inkbar = angular.element(this._navBarEl.querySelector('md-nav-ink-bar')); var self = this; this._$timeout(function() { self._updateTabs(self.mdSelectedNavItem, undefined); }); this._$scope.$watch('ctrl.mdSelectedNavItem', function(newValue, oldValue) { // Wait a digest before update tabs for products doing // anything dynamic in the template. self._$timeout(function() { self._updateTabs(newValue, oldValue); }); }); }; /** * Set the current tab to be selected. * @param {string|undefined} newValue New current tab name. * @param {string|undefined} oldValue Previous tab name. * @private */ MdNavBarController.prototype._updateTabs = function(newValue, oldValue) { var self = this; var tabs = this._getTabs(); // this._getTabs can return null if nav-bar has not yet been initialized if(!tabs) return; var oldIndex = -1; var newIndex = -1; var newTab = this._getTabByName(newValue); var oldTab = this._getTabByName(oldValue); if (oldTab) { oldTab.setSelected(false); oldIndex = tabs.indexOf(oldTab); } if (newTab) { newTab.setSelected(true); newIndex = tabs.indexOf(newTab); } this._$timeout(function() { self._updateInkBarStyles(newTab, newIndex, oldIndex); }); }; /** * Repositions the ink bar to the selected tab. * @private */ MdNavBarController.prototype._updateInkBarStyles = function(tab, newIndex, oldIndex) { this._inkbar.toggleClass('_md-left', newIndex < oldIndex) .toggleClass('_md-right', newIndex > oldIndex); this._inkbar.css({display: newIndex < 0 ? 'none' : ''}); if (tab) { var tabEl = tab.getButtonEl(); var left = tabEl.offsetLeft; this._inkbar.css({left: left + 'px', width: tabEl.offsetWidth + 'px'}); } }; /** * Returns an array of the current tabs. * @return {!Array} * @private */ MdNavBarController.prototype._getTabs = function() { var controllers = Array.prototype.slice.call( this._navBarEl.querySelectorAll('.md-nav-item')) .map(function(el) { return angular.element(el).controller('mdNavItem') }); return controllers.indexOf(undefined) ? controllers : null; }; /** * Returns the tab with the specified name. * @param {string} name The name of the tab, found in its name attribute. * @return {!NavItemController|undefined} * @private */ MdNavBarController.prototype._getTabByName = function(name) { return this._findTab(function(tab) { return tab.getName() == name; }); }; /** * Returns the selected tab. * @return {!NavItemController|undefined} * @private */ MdNavBarController.prototype._getSelectedTab = function() { return this._findTab(function(tab) { return tab.isSelected(); }); }; /** * Returns the focused tab. * @return {!NavItemController|undefined} */ MdNavBarController.prototype.getFocusedTab = function() { return this._findTab(function(tab) { return tab.hasFocus(); }); }; /** * Find a tab that matches the specified function. * @private */ MdNavBarController.prototype._findTab = function(fn) { var tabs = this._getTabs(); for (var i = 0; i < tabs.length; i++) { if (fn(tabs[i])) { return tabs[i]; } } return null; }; /** * Direct focus to the selected tab when focus enters the nav bar. */ MdNavBarController.prototype.onFocus = function() { var tab = this._getSelectedTab(); if (tab) { tab.setFocused(true); } }; /** * Move focus from oldTab to newTab. * @param {!NavItemController} oldTab * @param {!NavItemController} newTab * @private */ MdNavBarController.prototype._moveFocus = function(oldTab, newTab) { oldTab.setFocused(false); newTab.setFocused(true); }; /** * Responds to keypress events. * @param {!Event} e */ MdNavBarController.prototype.onKeydown = function(e) { var keyCodes = this._$mdConstant.KEY_CODE; var tabs = this._getTabs(); var focusedTab = this.getFocusedTab(); if (!focusedTab) return; var focusedTabIndex = tabs.indexOf(focusedTab); // use arrow keys to navigate between tabs switch (e.keyCode) { case keyCodes.UP_ARROW: case keyCodes.LEFT_ARROW: if (focusedTabIndex > 0) { this._moveFocus(focusedTab, tabs[focusedTabIndex - 1]); } break; case keyCodes.DOWN_ARROW: case keyCodes.RIGHT_ARROW: if (focusedTabIndex < tabs.length - 1) { this._moveFocus(focusedTab, tabs[focusedTabIndex + 1]); } break; case keyCodes.SPACE: case keyCodes.ENTER: // timeout to avoid a "digest already in progress" console error this._$timeout(function() { focusedTab.getButtonEl().click(); }); break; } }; /** * @ngInject */ function MdNavItem($mdAria, $$rAF) { return { restrict: 'E', require: ['mdNavItem', '^mdNavBar'], controller: MdNavItemController, bindToController: true, controllerAs: 'ctrl', replace: true, transclude: true, template: function(tElement, tAttrs) { var hasNavClick = tAttrs.mdNavClick; var hasNavHref = tAttrs.mdNavHref; var hasNavSref = tAttrs.mdNavSref; var hasSrefOpts = tAttrs.srefOpts; var navigationAttribute; var navigationOptions; var buttonTemplate; // Cannot specify more than one nav attribute if ((hasNavClick ? 1:0) + (hasNavHref ? 1:0) + (hasNavSref ? 1:0) > 1) { throw Error( 'Must not specify more than one of the md-nav-click, md-nav-href, ' + 'or md-nav-sref attributes per nav-item directive.' ); } if (hasNavClick) { navigationAttribute = 'ng-click="ctrl.mdNavClick()"'; } else if (hasNavHref) { navigationAttribute = 'ng-href="{{ctrl.mdNavHref}}"'; } else if (hasNavSref) { navigationAttribute = 'ui-sref="{{ctrl.mdNavSref}}"'; } navigationOptions = hasSrefOpts ? 'ui-sref-opts="{{ctrl.srefOpts}}" ' : ''; if (navigationAttribute) { buttonTemplate = '' + '' + '' + ''; } return '' + '
  • ' + (buttonTemplate || '') + '
  • '; }, scope: { 'mdNavClick': '&?', 'mdNavHref': '@?', 'mdNavSref': '@?', 'srefOpts': '=?', 'name': '@', }, link: function(scope, element, attrs, controllers) { // When accessing the element's contents synchronously, they // may not be defined yet because of transclusion. There is a higher // chance that it will be accessible if we wait one frame. $$rAF(function() { var mdNavItem = controllers[0]; var mdNavBar = controllers[1]; var navButton = angular.element(element[0].querySelector('._md-nav-button')); if (!mdNavItem.name) { mdNavItem.name = angular.element(element[0] .querySelector('._md-nav-button-text')).text().trim(); } navButton.on('click', function() { mdNavBar.mdSelectedNavItem = mdNavItem.name; scope.$apply(); }); $mdAria.expectWithText(element, 'aria-label'); }); } }; } /** * Controller for the nav-item component. * @param {!angular.JQLite} $element * @constructor * @final * @ngInject */ function MdNavItemController($element) { /** @private @const {!angular.JQLite} */ this._$element = $element; // Data-bound variables /** @const {?Function} */ this.mdNavClick; /** @const {?string} */ this.mdNavHref; /** @const {?string} */ this.mdNavSref; /** @const {?Object} */ this.srefOpts; /** @const {?string} */ this.name; // State variables /** @private {boolean} */ this._selected = false; /** @private {boolean} */ this._focused = false; } /** * Returns a map of class names and values for use by ng-class. * @return {!Object} */ MdNavItemController.prototype.getNgClassMap = function() { return { 'md-active': this._selected, 'md-primary': this._selected, 'md-unselected': !this._selected, 'md-focused': this._focused, }; }; /** * Get the name attribute of the tab. * @return {string} */ MdNavItemController.prototype.getName = function() { return this.name; }; /** * Get the button element associated with the tab. * @return {!Element} */ MdNavItemController.prototype.getButtonEl = function() { return this._$element[0].querySelector('._md-nav-button'); }; /** * Set the selected state of the tab. * @param {boolean} isSelected */ MdNavItemController.prototype.setSelected = function(isSelected) { this._selected = isSelected; }; /** * @return {boolean} */ MdNavItemController.prototype.isSelected = function() { return this._selected; }; /** * Set the focused state of the tab. * @param {boolean} isFocused */ MdNavItemController.prototype.setFocused = function(isFocused) { this._focused = isFocused; if (isFocused) { this.getButtonEl().focus(); } }; /** * @return {boolean} */ MdNavItemController.prototype.hasFocus = function() { return this._focused; }; })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.panel */ MdPanelService.$inject = ["presets", "$rootElement", "$rootScope", "$injector", "$window"]; angular .module('material.components.panel', [ 'material.core', 'material.components.backdrop' ]) .provider('$mdPanel', MdPanelProvider); /***************************************************************************** * PUBLIC DOCUMENTATION * *****************************************************************************/ /** * @ngdoc service * @name $mdPanelProvider * @module material.components.panel * * @description * `$mdPanelProvider` allows users to create configuration presets that will be * stored within a cached presets object. When the configuration is needed, the * user can request the preset by passing it as the first parameter in the * `$mdPanel.create` or `$mdPanel.open` methods. * * @usage * * (function(angular, undefined) { * 'use strict'; * * angular * .module('demoApp', ['ngMaterial']) * .config(DemoConfig) * .controller('DemoCtrl', DemoCtrl) * .controller('DemoMenuCtrl', DemoMenuCtrl); * * function DemoConfig($mdPanelProvider) { * $mdPanelProvider.definePreset('demoPreset', { * attachTo: angular.element(document.body), * controller: DemoMenuCtrl, * controllerAs: 'ctrl', * template: '' + * '', * panelClass: 'menu-panel-container', * focusOnOpen: false, * zIndex: 100, * propagateContainerEvents: true, * groupName: 'menus' * }); * } * * function PanelProviderCtrl($mdPanel) { * this.navigation = { * name: 'navigation', * items: [ * 'Home', * 'About', * 'Contact' * ] * }; * this.favorites = { * name: 'favorites', * items: [ * 'Add to Favorites' * ] * }; * this.more = { * name: 'more', * items: [ * 'Account', * 'Sign Out' * ] * }; * * $mdPanel.newPanelGroup('menus', { * maxOpen: 2 * }); * * this.showMenu = function($event, menu) { * $mdPanel.open('demoPreset', { * id: 'menu_' + menu.name, * position: $mdPanel.newPanelPosition() * .relativeTo($event.srcElement) * .addPanelPosition( * $mdPanel.xPosition.ALIGN_START, * $mdPanel.yPosition.BELOW * ), * locals: { * items: menu.items * }, * openFrom: $event * }); * }; * } * * function PanelMenuCtrl(mdPanelRef) { * // The controller is provided with an import named 'mdPanelRef' * this.closeMenu = function() { * mdPanelRef && mdPanelRef.close(); * }; * } * })(angular); * */ /** * @ngdoc method * @name $mdPanelProvider#definePreset * @description * Takes the passed in preset name and preset configuration object and adds it * to the `_presets` object of the provider. This `_presets` object is then * passed along to the `$mdPanel` service. * * @param {string} name Preset name. * @param {!Object} preset Specific configuration object that can contain any * and all of the parameters avaialble within the `$mdPanel.create` method. * However, parameters that pertain to id, position, animation, and user * interaction are not allowed and will be removed from the preset * configuration. */ /***************************************************************************** * MdPanel Service * *****************************************************************************/ /** * @ngdoc service * @name $mdPanel * @module material.components.panel * * @description * `$mdPanel` is a robust, low-level service for creating floating panels on * the screen. It can be used to implement tooltips, dialogs, pop-ups, etc. * * @usage * * (function(angular, undefined) { * 'use strict'; * * angular * .module('demoApp', ['ngMaterial']) * .controller('DemoDialogController', DialogController); * * var panelRef; * * function showPanel($event) { * var panelPosition = $mdPanel.newPanelPosition() * .absolute() * .top('50%') * .left('50%'); * * var panelAnimation = $mdPanel.newPanelAnimation() * .targetEvent($event) * .defaultAnimation('md-panel-animate-fly') * .closeTo('.show-button'); * * var config = { * attachTo: angular.element(document.body), * controller: DialogController, * controllerAs: 'ctrl', * position: panelPosition, * animation: panelAnimation, * targetEvent: $event, * templateUrl: 'dialog-template.html', * clickOutsideToClose: true, * escapeToClose: true, * focusOnOpen: true * }; * * $mdPanel.open(config) * .then(function(result) { * panelRef = result; * }); * } * * function DialogController(MdPanelRef) { * function closeDialog() { * if (MdPanelRef) MdPanelRef.close(); * } * } * })(angular); * */ /** * @ngdoc method * @name $mdPanel#create * @description * Creates a panel with the specified options. * * @param config {!Object=} Specific configuration object that may contain the * following properties: * * - `id` - `{string=}`: An ID to track the panel by. When an ID is provided, * the created panel is added to a tracked panels object. Any subsequent * requests made to create a panel with that ID are ignored. This is useful * in having the panel service not open multiple panels from the same user * interaction when there is no backdrop and events are propagated. Defaults * to an arbitrary string that is not tracked. * - `template` - `{string=}`: HTML template to show in the panel. This * **must** be trusted HTML with respect to AngularJS’s * [$sce service](https://docs.angularjs.org/api/ng/service/$sce). * - `templateUrl` - `{string=}`: The URL that will be used as the content of * the panel. * - `contentElement` - `{(string|!angular.JQLite|!Element)=}`: Pre-compiled * element to be used as the panel's content. * - `controller` - `{(function|string)=}`: The controller to associate with * the panel. The controller can inject a reference to the returned * panelRef, which allows the panel to be closed, hidden, and shown. Any * fields passed in through locals or resolve will be bound to the * controller. * - `controllerAs` - `{string=}`: An alias to assign the controller to on * the scope. * - `bindToController` - `{boolean=}`: Binds locals to the controller * instead of passing them in. Defaults to true, as this is a best * practice. * - `locals` - `{Object=}`: An object containing key/value pairs. The keys * will be used as names of values to inject into the controller. For * example, `locals: {three: 3}` would inject `three` into the controller, * with the value 3. 'mdPanelRef' is a reserved key, and will always * be set to the created MdPanelRef instance. * - `resolve` - `{Object=}`: Similar to locals, except it takes promises as * values. The panel will not open until all of the promises resolve. * - `attachTo` - `{(string|!angular.JQLite|!Element)=}`: The element to * attach the panel to. Defaults to appending to the root element of the * application. * - `propagateContainerEvents` - `{boolean=}`: Whether pointer or touch * events should be allowed to propagate 'go through' the container, aka the * wrapper, of the panel. Defaults to false. * - `panelClass` - `{string=}`: A css class to apply to the panel element. * This class should define any borders, box-shadow, etc. for the panel. * - `zIndex` - `{number=}`: The z-index to place the panel at. * Defaults to 80. * - `position` - `{MdPanelPosition=}`: An MdPanelPosition object that * specifies the alignment of the panel. For more information, see * `MdPanelPosition`. * - `clickOutsideToClose` - `{boolean=}`: Whether the user can click * outside the panel to close it. Defaults to false. * - `escapeToClose` - `{boolean=}`: Whether the user can press escape to * close the panel. Defaults to false. * - `onCloseSuccess` - `{function(!panelRef, string)=}`: Function that is * called after the close successfully finishes. The first parameter passed * into this function is the current panelRef and the 2nd is an optional * string explaining the close reason. The currently supported closeReasons * can be found in the MdPanelRef.closeReasons enum. These are by default * passed along by the panel. * - `trapFocus` - `{boolean=}`: Whether focus should be trapped within the * panel. If `trapFocus` is true, the user will not be able to interact * with the rest of the page until the panel is dismissed. Defaults to * false. * - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on * open. Only disable if focusing some other way, as focus management is * required for panels to be accessible. Defaults to true. * - `fullscreen` - `{boolean=}`: Whether the panel should be full screen. * Applies the class `._md-panel-fullscreen` to the panel on open. Defaults * to false. * - `animation` - `{MdPanelAnimation=}`: An MdPanelAnimation object that * specifies the animation of the panel. For more information, see * `MdPanelAnimation`. * - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop * behind the panel. Defaults to false. * - `disableParentScroll` - `{boolean=}`: Whether the user can scroll the * page behind the panel. Defaults to false. * - `onDomAdded` - `{function=}`: Callback function used to announce when * the panel is added to the DOM. * - `onOpenComplete` - `{function=}`: Callback function used to announce * when the open() action is finished. * - `onRemoving` - `{function=}`: Callback function used to announce the * close/hide() action is starting. * - `onDomRemoved` - `{function=}`: Callback function used to announce when * the panel is removed from the DOM. * - `origin` - `{(string|!angular.JQLite|!Element)=}`: The element to focus * on when the panel closes. This is commonly the element which triggered * the opening of the panel. If you do not use `origin`, you need to control * the focus manually. * - `groupName` - `{(string|!Array)=}`: A group name or an array of * group names. The group name is used for creating a group of panels. The * group is used for configuring the number of open panels and identifying * specific behaviors for groups. For instance, all tooltips could be * identified using the same groupName. * * @returns {!MdPanelRef} panelRef */ /** * @ngdoc method * @name $mdPanel#open * @description * Calls the create method above, then opens the panel. This is a shortcut for * creating and then calling open manually. If custom methods need to be * called when the panel is added to the DOM or opened, do not use this method. * Instead create the panel, chain promises on the domAdded and openComplete * methods, and call open from the returned panelRef. * * @param {!Object=} config Specific configuration object that may contain * the properties defined in `$mdPanel.create`. * @returns {!angular.$q.Promise} panelRef A promise that resolves * to an instance of the panel. */ /** * @ngdoc method * @name $mdPanel#newPanelPosition * @description * Returns a new instance of the MdPanelPosition object. Use this to create * the position config object. * * @returns {!MdPanelPosition} panelPosition */ /** * @ngdoc method * @name $mdPanel#newPanelAnimation * @description * Returns a new instance of the MdPanelAnimation object. Use this to create * the animation config object. * * @returns {!MdPanelAnimation} panelAnimation */ /** * @ngdoc method * @name $mdPanel#newPanelGroup * @description * Creates a panel group and adds it to a tracked list of panel groups. * * @param {string} groupName Name of the group to create. * @param {!Object=} config Specific configuration object that may contain the * following properties: * * - `maxOpen` - `{number=}`: The maximum number of panels that are allowed to * be open within a defined panel group. * * @returns {!Object, * openPanels: !Array, * maxOpen: number}>} panelGroup */ /** * @ngdoc method * @name $mdPanel#setGroupMaxOpen * @description * Sets the maximum number of panels in a group that can be opened at a given * time. * * @param {string} groupName The name of the group to configure. * @param {number} maxOpen The maximum number of panels that can be * opened. Infinity can be passed in to remove the maxOpen limit. */ /***************************************************************************** * MdPanelRef * *****************************************************************************/ /** * @ngdoc type * @name MdPanelRef * @module material.components.panel * @description * A reference to a created panel. This reference contains a unique id for the * panel, along with the following properties: * * - `id` - `{string}`: The unique id for the panel. This id is used to track * when a panel was interacted with. * - `config` - `{!Object=}`: The entire config object that was used in * create. * - `isAttached` - `{boolean}`: Whether the panel is attached to the DOM. * Visibility to the user does not factor into isAttached. * - `panelContainer` - `{angular.JQLite}`: The wrapper element containing the * panel. This property is added in order to have access to the `addClass`, * `removeClass`, `toggleClass`, etc methods. * - `panelEl` - `{angular.JQLite}`: The panel element. This property is added * in order to have access to the `addClass`, `removeClass`, `toggleClass`, * etc methods. */ /** * @ngdoc method * @name MdPanelRef#open * @description * Attaches and shows the panel. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * opened. */ /** * @ngdoc method * @name MdPanelRef#close * @description * Hides and detaches the panel. Note that this will **not** destroy the panel. * If you don't intend on using the panel again, call the {@link #destroy * destroy} method afterwards. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * closed. */ /** * @ngdoc method * @name MdPanelRef#attach * @description * Create the panel elements and attach them to the DOM. The panel will be * hidden by default. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * attached. */ /** * @ngdoc method * @name MdPanelRef#detach * @description * Removes the panel from the DOM. This will NOT hide the panel before removing * it. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * detached. */ /** * @ngdoc method * @name MdPanelRef#show * @description * Shows the panel. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * shown and animations are completed. */ /** * @ngdoc method * @name MdPanelRef#hide * @description * Hides the panel. * * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * hidden and animations are completed. */ /** * @ngdoc method * @name MdPanelRef#destroy * @description * Destroys the panel. The panel cannot be opened again after this is called. */ /** * @ngdoc method * @name MdPanelRef#addClass * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * @description * Adds a class to the panel. DO NOT use this hide/show the panel. * * @param {string} newClass class to be added. * @param {boolean} toElement Whether or not to add the class to the panel * element instead of the container. */ /** * @ngdoc method * @name MdPanelRef#removeClass * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * @description * Removes a class from the panel. DO NOT use this to hide/show the panel. * * @param {string} oldClass Class to be removed. * @param {boolean} fromElement Whether or not to remove the class from the * panel element instead of the container. */ /** * @ngdoc method * @name MdPanelRef#toggleClass * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * @description * Toggles a class on the panel. DO NOT use this to hide/show the panel. * * @param {string} toggleClass Class to be toggled. * @param {boolean} onElement Whether or not to remove the class from the panel * element instead of the container. */ /** * @ngdoc method * @name MdPanelRef#updatePosition * @description * Updates the position configuration of a panel. Use this to update the * position of a panel that is open, without having to close and re-open the * panel. * * @param {!MdPanelPosition} position */ /** * @ngdoc method * @name MdPanelRef#addToGroup * @description * Adds a panel to a group if the panel does not exist within the group already. * A panel can only exist within a single group. * * @param {string} groupName The name of the group to add the panel to. */ /** * @ngdoc method * @name MdPanelRef#removeFromGroup * @description * Removes a panel from a group if the panel exists within that group. The group * must be created ahead of time. * * @param {string} groupName The name of the group. */ /** * @ngdoc method * @name MdPanelRef#registerInterceptor * @description * Registers an interceptor with the panel. The callback should return a promise, * which will allow the action to continue when it gets resolved, or will * prevent an action if it is rejected. The interceptors are called sequentially * and it reverse order. `type` must be one of the following * values available on `$mdPanel.interceptorTypes`: * * `CLOSE` - Gets called before the panel begins closing. * * @param {string} type Type of interceptor. * @param {!angular.$q.Promise} callback Callback to be registered. * @returns {!MdPanelRef} */ /** * @ngdoc method * @name MdPanelRef#removeInterceptor * @description * Removes a registered interceptor. * * @param {string} type Type of interceptor to be removed. * @param {function(): !angular.$q.Promise} callback Interceptor to be removed. * @returns {!MdPanelRef} */ /** * @ngdoc method * @name MdPanelRef#removeAllInterceptors * @description * Removes all interceptors. If a type is supplied, only the * interceptors of that type will be cleared. * * @param {string=} type Type of interceptors to be removed. * @returns {!MdPanelRef} */ /** * @ngdoc method * @name MdPanelRef#updateAnimation * @description * Updates the animation configuration for a panel. You can use this to change * the panel's animation without having to re-create it. * * @param {!MdPanelAnimation} animation */ /***************************************************************************** * MdPanelPosition * *****************************************************************************/ /** * @ngdoc type * @name MdPanelPosition * @module material.components.panel * @description * * Object for configuring the position of the panel. * * @usage * * #### Centering the panel * * * new MdPanelPosition().absolute().center(); * * * #### Overlapping the panel with an element * * * new MdPanelPosition() * .relativeTo(someElement) * .addPanelPosition( * $mdPanel.xPosition.ALIGN_START, * $mdPanel.yPosition.ALIGN_TOPS * ); * * * #### Aligning the panel with the bottom of an element * * * new MdPanelPosition() * .relativeTo(someElement) * .addPanelPosition($mdPanel.xPosition.CENTER, $mdPanel.yPosition.BELOW); * */ /** * @ngdoc method * @name MdPanelPosition#absolute * @description * Positions the panel absolutely relative to the parent element. If the parent * is document.body, this is equivalent to positioning the panel absolutely * within the viewport. * * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#relativeTo * @description * Positions the panel relative to a specific element. * * @param {string|!Element|!angular.JQLite} element Query selector, DOM element, * or angular element to position the panel with respect to. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#top * @description * Sets the value of `top` for the panel. Clears any previously set vertical * position. * * @param {string=} top Value of `top`. Defaults to '0'. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#bottom * @description * Sets the value of `bottom` for the panel. Clears any previously set vertical * position. * * @param {string=} bottom Value of `bottom`. Defaults to '0'. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#start * @description * Sets the panel to the start of the page - `left` if `ltr` or `right` for * `rtl`. Clears any previously set horizontal position. * * @param {string=} start Value of position. Defaults to '0'. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#end * @description * Sets the panel to the end of the page - `right` if `ltr` or `left` for `rtl`. * Clears any previously set horizontal position. * * @param {string=} end Value of position. Defaults to '0'. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#left * @description * Sets the value of `left` for the panel. Clears any previously set * horizontal position. * * @param {string=} left Value of `left`. Defaults to '0'. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#right * @description * Sets the value of `right` for the panel. Clears any previously set * horizontal position. * * @param {string=} right Value of `right`. Defaults to '0'. * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#centerHorizontally * @description * Centers the panel horizontally in the viewport. Clears any previously set * horizontal position. * * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#centerVertically * @description * Centers the panel vertically in the viewport. Clears any previously set * vertical position. * * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#center * @description * Centers the panel horizontally and vertically in the viewport. This is * equivalent to calling both `centerHorizontally` and `centerVertically`. * Clears any previously set horizontal and vertical positions. * * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#addPanelPosition * @description * Sets the x and y position for the panel relative to another element. Can be * called multiple times to specify an ordered list of panel positions. The * first position which allows the panel to be completely on-screen will be * chosen; the last position will be chose whether it is on-screen or not. * * xPosition must be one of the following values available on * $mdPanel.xPosition: * * * CENTER | ALIGN_START | ALIGN_END | OFFSET_START | OFFSET_END * *
     *    *************
     *    *           *
     *    *   PANEL   *
     *    *           *
     *    *************
     *   A B    C    D E
     *
     * A: OFFSET_START (for LTR displays)
     * B: ALIGN_START (for LTR displays)
     * C: CENTER
     * D: ALIGN_END (for LTR displays)
     * E: OFFSET_END (for LTR displays)
     * 
    * * yPosition must be one of the following values available on * $mdPanel.yPosition: * * CENTER | ALIGN_TOPS | ALIGN_BOTTOMS | ABOVE | BELOW * *
     *   F
     *   G *************
     *     *           *
     *   H *   PANEL   *
     *     *           *
     *   I *************
     *   J
     *
     * F: BELOW
     * G: ALIGN_TOPS
     * H: CENTER
     * I: ALIGN_BOTTOMS
     * J: ABOVE
     * 
    * * @param {string} xPosition * @param {string} yPosition * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#withOffsetX * @description * Sets the value of the offset in the x-direction. * * @param {string} offsetX * @returns {!MdPanelPosition} */ /** * @ngdoc method * @name MdPanelPosition#withOffsetY * @description * Sets the value of the offset in the y-direction. * * @param {string} offsetY * @returns {!MdPanelPosition} */ /***************************************************************************** * MdPanelAnimation * *****************************************************************************/ /** * @ngdoc type * @name MdPanelAnimation * @module material.components.panel * @description * Animation configuration object. To use, create an MdPanelAnimation with the * desired properties, then pass the object as part of $mdPanel creation. * * @usage * * * var panelAnimation = new MdPanelAnimation() * .openFrom(myButtonEl) * .duration(1337) * .closeTo('.my-button') * .withAnimation($mdPanel.animation.SCALE); * * $mdPanel.create({ * animation: panelAnimation * }); * */ /** * @ngdoc method * @name MdPanelAnimation#openFrom * @description * Specifies where to start the open animation. `openFrom` accepts a * click event object, query selector, DOM element, or a Rect object that * is used to determine the bounds. When passed a click event, the location * of the click will be used as the position to start the animation. * * @param {string|!Element|!Event|{top: number, left: number}} * @returns {!MdPanelAnimation} */ /** * @ngdoc method * @name MdPanelAnimation#closeTo * @description * Specifies where to animate the panel close. `closeTo` accepts a * query selector, DOM element, or a Rect object that is used to determine * the bounds. * * @param {string|!Element|{top: number, left: number}} * @returns {!MdPanelAnimation} */ /** * @ngdoc method * @name MdPanelAnimation#withAnimation * @description * Specifies the animation class. * * There are several default animations that can be used: * ($mdPanel.animation) * SLIDE: The panel slides in and out from the specified * elements. It will not fade in or out. * SCALE: The panel scales in and out. Slide and fade are * included in this animation. * FADE: The panel fades in and out. * * Custom classes will by default fade in and out unless * "transition: opacity 1ms" is added to the to custom class. * * @param {string|{open: string, close: string}} cssClass * @returns {!MdPanelAnimation} */ /** * @ngdoc method * @name MdPanelAnimation#duration * @description * Specifies the duration of the animation in milliseconds. The `duration` * method accepts either a number or an object with separate open and close * durations. * * @param {number|{open: number, close: number}} duration * @returns {!MdPanelAnimation} */ /***************************************************************************** * PUBLIC DOCUMENTATION * *****************************************************************************/ var MD_PANEL_Z_INDEX = 80; var MD_PANEL_HIDDEN = '_md-panel-hidden'; var FOCUS_TRAP_TEMPLATE = angular.element( '
    '); var _presets = {}; /** * A provider that is used for creating presets for the panel API. * @final @constructor @ngInject */ function MdPanelProvider() { return { 'definePreset': definePreset, 'getAllPresets': getAllPresets, 'clearPresets': clearPresets, '$get': $getProvider() }; } /** * Takes the passed in panel configuration object and adds it to the `_presets` * object at the specified name. * @param {string} name Name of the preset to set. * @param {!Object} preset Specific configuration object that can contain any * and all of the parameters avaialble within the `$mdPanel.create` method. * However, parameters that pertain to id, position, animation, and user * interaction are not allowed and will be removed from the preset * configuration. */ function definePreset(name, preset) { if (!name || !preset) { throw new Error('mdPanelProvider: The panel preset definition is ' + 'malformed. The name and preset object are required.'); } else if (_presets.hasOwnProperty(name)) { throw new Error('mdPanelProvider: The panel preset you have requested ' + 'has already been defined.'); } // Delete any property on the preset that is not allowed. delete preset.id; delete preset.position; delete preset.animation; _presets[name] = preset; } /** * Gets a clone of the `_presets`. * @return {!Object} */ function getAllPresets() { return angular.copy(_presets); } /** * Clears all of the stored presets. */ function clearPresets() { _presets = {}; } /** * Represents the `$get` method of the AngularJS provider. From here, a new * reference to the MdPanelService is returned where the needed arguments are * passed in including the MdPanelProvider `_presets`. * @param {!Object} _presets * @param {!angular.JQLite} $rootElement * @param {!angular.Scope} $rootScope * @param {!angular.$injector} $injector * @param {!angular.$window} $window */ function $getProvider() { return [ '$rootElement', '$rootScope', '$injector', '$window', function($rootElement, $rootScope, $injector, $window) { return new MdPanelService(_presets, $rootElement, $rootScope, $injector, $window); } ]; } /***************************************************************************** * MdPanel Service * *****************************************************************************/ /** * A service that is used for controlling/displaying panels on the screen. * @param {!Object} presets * @param {!angular.JQLite} $rootElement * @param {!angular.Scope} $rootScope * @param {!angular.$injector} $injector * @param {!angular.$window} $window * @final @constructor @ngInject */ function MdPanelService(presets, $rootElement, $rootScope, $injector, $window) { /** * Default config options for the panel. * Anything angular related needs to be done later. Therefore * scope: $rootScope.$new(true), * attachTo: $rootElement, * are added later. * @private {!Object} */ this._defaultConfigOptions = { bindToController: true, clickOutsideToClose: false, disableParentScroll: false, escapeToClose: false, focusOnOpen: true, fullscreen: false, hasBackdrop: false, propagateContainerEvents: false, transformTemplate: angular.bind(this, this._wrapTemplate), trapFocus: false, zIndex: MD_PANEL_Z_INDEX }; /** @private {!Object} */ this._config = {}; /** @private {!Object} */ this._presets = presets; /** @private @const */ this._$rootElement = $rootElement; /** @private @const */ this._$rootScope = $rootScope; /** @private @const */ this._$injector = $injector; /** @private @const */ this._$window = $window; /** @private @const */ this._$mdUtil = this._$injector.get('$mdUtil'); /** @private {!Object} */ this._trackedPanels = {}; /** * @private {!Object, * openPanels: !Array, * maxOpen: number}>} */ this._groups = Object.create(null); /** * Default animations that can be used within the panel. * @type {enum} */ this.animation = MdPanelAnimation.animation; /** * Possible values of xPosition for positioning the panel relative to * another element. * @type {enum} */ this.xPosition = MdPanelPosition.xPosition; /** * Possible values of yPosition for positioning the panel relative to * another element. * @type {enum} */ this.yPosition = MdPanelPosition.yPosition; /** * Possible values for the interceptors that can be registered on a panel. * @type {enum} */ this.interceptorTypes = MdPanelRef.interceptorTypes; /** * Possible values for closing of a panel. * @type {enum} */ this.closeReasons = MdPanelRef.closeReasons; /** * Possible values of absolute position. * @type {enum} */ this.absPosition = MdPanelPosition.absPosition; } /** * Creates a panel with the specified options. * @param {string=} preset Name of a preset configuration that can be used to * extend the panel configuration. * @param {!Object=} config Configuration object for the panel. * @returns {!MdPanelRef} */ MdPanelService.prototype.create = function(preset, config) { if (typeof preset === 'string') { preset = this._getPresetByName(preset); } else if (typeof preset === 'object' && (angular.isUndefined(config) || !config)) { config = preset; preset = {}; } preset = preset || {}; config = config || {}; // If the passed-in config contains an ID and the ID is within _trackedPanels, // return the tracked panel after updating its config with the passed-in // config. if (angular.isDefined(config.id) && this._trackedPanels[config.id]) { var trackedPanel = this._trackedPanels[config.id]; angular.extend(trackedPanel.config, config); return trackedPanel; } // Combine the passed-in config, the _defaultConfigOptions, and the preset // configuration into the `_config`. this._config = angular.extend({ // If no ID is set within the passed-in config, then create an arbitrary ID. id: config.id || 'panel_' + this._$mdUtil.nextUid(), scope: this._$rootScope.$new(true), attachTo: this._$rootElement }, this._defaultConfigOptions, config, preset); // Create the panelRef and add it to the `_trackedPanels` object. var panelRef = new MdPanelRef(this._config, this._$injector); this._trackedPanels[config.id] = panelRef; // Add the panel to each of its requested groups. if (this._config.groupName) { if (angular.isString(this._config.groupName)) { this._config.groupName = [this._config.groupName]; } angular.forEach(this._config.groupName, function(group) { panelRef.addToGroup(group); }); } this._config.scope.$on('$destroy', angular.bind(panelRef, panelRef.detach)); return panelRef; }; /** * Creates and opens a panel with the specified options. * @param {string=} preset Name of a preset configuration that can be used to * extend the panel configuration. * @param {!Object=} config Configuration object for the panel. * @returns {!angular.$q.Promise} The panel created from create. */ MdPanelService.prototype.open = function(preset, config) { var panelRef = this.create(preset, config); return panelRef.open().then(function() { return panelRef; }); }; /** * Gets a specific preset configuration object saved within `_presets`. * @param {string} preset Name of the preset to search for. * @returns {!Object} The preset configuration object. */ MdPanelService.prototype._getPresetByName = function(preset) { if (!this._presets[preset]) { throw new Error('mdPanel: The panel preset configuration that you ' + 'requested does not exist. Use the $mdPanelProvider to create a ' + 'preset before requesting one.'); } return this._presets[preset]; }; /** * Returns a new instance of the MdPanelPosition. Use this to create the * positioning object. * @returns {!MdPanelPosition} */ MdPanelService.prototype.newPanelPosition = function() { return new MdPanelPosition(this._$injector); }; /** * Returns a new instance of the MdPanelAnimation. Use this to create the * animation object. * @returns {!MdPanelAnimation} */ MdPanelService.prototype.newPanelAnimation = function() { return new MdPanelAnimation(this._$injector); }; /** * Creates a panel group and adds it to a tracked list of panel groups. * @param groupName {string} Name of the group to create. * @param config {!Object=} Specific configuration object that may contain the * following properties: * * - `maxOpen` - `{number=}`: The maximum number of panels that are allowed * open within a defined panel group. * * @returns {!Object, * openPanels: !Array, * maxOpen: number}>} panelGroup */ MdPanelService.prototype.newPanelGroup = function(groupName, config) { if (!this._groups[groupName]) { config = config || {}; var group = { panels: [], openPanels: [], maxOpen: config.maxOpen > 0 ? config.maxOpen : Infinity }; this._groups[groupName] = group; } return this._groups[groupName]; }; /** * Sets the maximum number of panels in a group that can be opened at a given * time. * @param {string} groupName The name of the group to configure. * @param {number} maxOpen The maximum number of panels that can be * opened. Infinity can be passed in to remove the maxOpen limit. */ MdPanelService.prototype.setGroupMaxOpen = function(groupName, maxOpen) { if (this._groups[groupName]) { this._groups[groupName].maxOpen = maxOpen; } else { throw new Error('mdPanel: Group does not exist yet. Call newPanelGroup().'); } }; /** * Determines if the current number of open panels within a group exceeds the * limit of allowed open panels. * @param {string} groupName The name of the group to check. * @returns {boolean} true if open count does exceed maxOpen and false if not. * @private */ MdPanelService.prototype._openCountExceedsMaxOpen = function(groupName) { if (this._groups[groupName]) { var group = this._groups[groupName]; return group.maxOpen > 0 && group.openPanels.length > group.maxOpen; } return false; }; /** * Closes the first open panel within a specific group. * @param {string} groupName The name of the group. * @private */ MdPanelService.prototype._closeFirstOpenedPanel = function(groupName) { this._groups[groupName].openPanels[0].close(); }; /** * Wraps the users template in two elements, md-panel-outer-wrapper, which * covers the entire attachTo element, and md-panel, which contains only the * template. This allows the panel control over positioning, animations, * and similar properties. * @param {string} origTemplate The original template. * @returns {string} The wrapped template. * @private */ MdPanelService.prototype._wrapTemplate = function(origTemplate) { var template = origTemplate || ''; // The panel should be initially rendered offscreen so we can calculate // height and width for positioning. return '' + '
    ' + '
    ' + template + '
    ' + '
    '; }; /** * Wraps a content element in a md-panel-outer wrapper and * positions it off-screen. Allows for proper control over positoning * and animations. * @param {!angular.JQLite} contentElement Element to be wrapped. * @return {!angular.JQLite} Wrapper element. * @private */ MdPanelService.prototype._wrapContentElement = function(contentElement) { var wrapper = angular.element('
    '); contentElement.addClass('md-panel _md-panel-offscreen'); wrapper.append(contentElement); return wrapper; }; /***************************************************************************** * MdPanelRef * *****************************************************************************/ /** * A reference to a created panel. This reference contains a unique id for the * panel, along with properties/functions used to control the panel. * @param {!Object} config * @param {!angular.$injector} $injector * @final @constructor */ function MdPanelRef(config, $injector) { // Injected variables. /** @private @const {!angular.$q} */ this._$q = $injector.get('$q'); /** @private @const {!angular.$mdCompiler} */ this._$mdCompiler = $injector.get('$mdCompiler'); /** @private @const {!angular.$mdConstant} */ this._$mdConstant = $injector.get('$mdConstant'); /** @private @const {!angular.$mdUtil} */ this._$mdUtil = $injector.get('$mdUtil'); /** @private @const {!angular.$mdTheming} */ this._$mdTheming = $injector.get('$mdTheming'); /** @private @const {!angular.Scope} */ this._$rootScope = $injector.get('$rootScope'); /** @private @const {!angular.$animate} */ this._$animate = $injector.get('$animate'); /** @private @const {!MdPanelRef} */ this._$mdPanel = $injector.get('$mdPanel'); /** @private @const {!angular.$log} */ this._$log = $injector.get('$log'); /** @private @const {!angular.$window} */ this._$window = $injector.get('$window'); /** @private @const {!Function} */ this._$$rAF = $injector.get('$$rAF'); // Public variables. /** * Unique id for the panelRef. * @type {string} */ this.id = config.id; /** @type {!Object} */ this.config = config; /** @type {!angular.JQLite|undefined} */ this.panelContainer; /** @type {!angular.JQLite|undefined} */ this.panelEl; /** * Whether the panel is attached. This is synchronous. When attach is called, * isAttached is set to true. When detach is called, isAttached is set to * false. * @type {boolean} */ this.isAttached = false; // Private variables. /** @private {Array} */ this._removeListeners = []; /** @private {!angular.JQLite|undefined} */ this._topFocusTrap; /** @private {!angular.JQLite|undefined} */ this._bottomFocusTrap; /** @private {!$mdPanel|undefined} */ this._backdropRef; /** @private {Function?} */ this._restoreScroll = null; /** * Keeps track of all the panel interceptors. * @private {!Object} */ this._interceptors = Object.create(null); /** * Cleanup function, provided by `$mdCompiler` and assigned after the element * has been compiled. When `contentElement` is used, the function is used to * restore the element to it's proper place in the DOM. * @private {!Function} */ this._compilerCleanup = null; /** * Cache for saving and restoring element inline styles, CSS classes etc. * @type {{styles: string, classes: string}} */ this._restoreCache = { styles: '', classes: '' }; } MdPanelRef.interceptorTypes = { CLOSE: 'onClose' }; /** * Opens an already created and configured panel. If the panel is already * visible, does nothing. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is opened and animations finish. */ MdPanelRef.prototype.open = function() { var self = this; return this._$q(function(resolve, reject) { var done = self._done(resolve, self); var show = self._simpleBind(self.show, self); var checkGroupMaxOpen = function() { if (self.config.groupName) { angular.forEach(self.config.groupName, function(group) { if (self._$mdPanel._openCountExceedsMaxOpen(group)) { self._$mdPanel._closeFirstOpenedPanel(group); } }); } }; self.attach() .then(show) .then(checkGroupMaxOpen) .then(done) .catch(reject); }); }; /** * Closes the panel. * @param {string} closeReason The event type that triggered the close. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is closed and animations finish. */ MdPanelRef.prototype.close = function(closeReason) { var self = this; return this._$q(function(resolve, reject) { self._callInterceptors(MdPanelRef.interceptorTypes.CLOSE).then(function() { var done = self._done(resolve, self); var detach = self._simpleBind(self.detach, self); var onCloseSuccess = self.config['onCloseSuccess'] || angular.noop; onCloseSuccess = angular.bind(self, onCloseSuccess, self, closeReason); self.hide() .then(detach) .then(done) .then(onCloseSuccess) .catch(reject); }, reject); }); }; /** * Attaches the panel. The panel will be hidden afterwards. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is attached. */ MdPanelRef.prototype.attach = function() { if (this.isAttached && this.panelEl) { return this._$q.when(this); } var self = this; return this._$q(function(resolve, reject) { var done = self._done(resolve, self); var onDomAdded = self.config['onDomAdded'] || angular.noop; var addListeners = function(response) { self.isAttached = true; self._addEventListeners(); return response; }; self._$q.all([ self._createBackdrop(), self._createPanel() .then(addListeners) .catch(reject) ]).then(onDomAdded) .then(done) .catch(reject); }); }; /** * Only detaches the panel. Will NOT hide the panel first. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel is detached. */ MdPanelRef.prototype.detach = function() { if (!this.isAttached) { return this._$q.when(this); } var self = this; var onDomRemoved = self.config['onDomRemoved'] || angular.noop; var detachFn = function() { self._removeEventListeners(); // Remove the focus traps that we added earlier for keeping focus within // the panel. if (self._topFocusTrap && self._topFocusTrap.parentNode) { self._topFocusTrap.parentNode.removeChild(self._topFocusTrap); } if (self._bottomFocusTrap && self._bottomFocusTrap.parentNode) { self._bottomFocusTrap.parentNode.removeChild(self._bottomFocusTrap); } if (self._restoreCache.classes) { self.panelEl[0].className = self._restoreCache.classes; } // Either restore the saved styles or clear the ones set by mdPanel. self.panelEl[0].style.cssText = self._restoreCache.styles || ''; self._compilerCleanup(); self.panelContainer.remove(); self.isAttached = false; return self._$q.when(self); }; if (this._restoreScroll) { this._restoreScroll(); this._restoreScroll = null; } return this._$q(function(resolve, reject) { var done = self._done(resolve, self); self._$q.all([ detachFn(), self._backdropRef ? self._backdropRef.detach() : true ]).then(onDomRemoved) .then(done) .catch(reject); }); }; /** * Destroys the panel. The Panel cannot be opened again after this. */ MdPanelRef.prototype.destroy = function() { var self = this; if (this.config.groupName) { angular.forEach(this.config.groupName, function(group) { self.removeFromGroup(group); }); } this.config.scope.$destroy(); this.config.locals = null; this._interceptors = null; }; /** * Shows the panel. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel has shown and animations finish. */ MdPanelRef.prototype.show = function() { if (!this.panelContainer) { return this._$q(function(resolve, reject) { reject('mdPanel: Panel does not exist yet. Call open() or attach().'); }); } if (!this.panelContainer.hasClass(MD_PANEL_HIDDEN)) { return this._$q.when(this); } var self = this; var animatePromise = function() { self.panelContainer.removeClass(MD_PANEL_HIDDEN); return self._animateOpen(); }; return this._$q(function(resolve, reject) { var done = self._done(resolve, self); var onOpenComplete = self.config['onOpenComplete'] || angular.noop; var addToGroupOpen = function() { if (self.config.groupName) { angular.forEach(self.config.groupName, function(group) { self._$mdPanel._groups[group].openPanels.push(self); }); } }; self._$q.all([ self._backdropRef ? self._backdropRef.show() : self, animatePromise().then(function() { self._focusOnOpen(); }, reject) ]).then(onOpenComplete) .then(addToGroupOpen) .then(done) .catch(reject); }); }; /** * Hides the panel. * @returns {!angular.$q.Promise} A promise that is resolved when * the panel has hidden and animations finish. */ MdPanelRef.prototype.hide = function() { if (!this.panelContainer) { return this._$q(function(resolve, reject) { reject('mdPanel: Panel does not exist yet. Call open() or attach().'); }); } if (this.panelContainer.hasClass(MD_PANEL_HIDDEN)) { return this._$q.when(this); } var self = this; return this._$q(function(resolve, reject) { var done = self._done(resolve, self); var onRemoving = self.config['onRemoving'] || angular.noop; var hidePanel = function() { self.panelContainer.addClass(MD_PANEL_HIDDEN); }; var removeFromGroupOpen = function() { if (self.config.groupName) { var group, index; angular.forEach(self.config.groupName, function(group) { group = self._$mdPanel._groups[group]; index = group.openPanels.indexOf(self); if (index > -1) { group.openPanels.splice(index, 1); } }); } }; var focusOnOrigin = function() { var origin = self.config['origin']; if (origin) { getElement(origin).focus(); } }; self._$q.all([ self._backdropRef ? self._backdropRef.hide() : self, self._animateClose() .then(onRemoving) .then(hidePanel) .then(removeFromGroupOpen) .then(focusOnOrigin) .catch(reject) ]).then(done, reject); }); }; /** * Add a class to the panel. DO NOT use this to hide/show the panel. * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * * @param {string} newClass Class to be added. * @param {boolean} toElement Whether or not to add the class to the panel * element instead of the container. */ MdPanelRef.prototype.addClass = function(newClass, toElement) { this._$log.warn( 'mdPanel: The addClass method is in the process of being deprecated. ' + 'Full deprecation is scheduled for the AngularJS Material 1.2 release. ' + 'To achieve the same results, use the panelContainer or panelEl ' + 'JQLite elements that are referenced in MdPanelRef.'); if (!this.panelContainer) { throw new Error( 'mdPanel: Panel does not exist yet. Call open() or attach().'); } if (!toElement && !this.panelContainer.hasClass(newClass)) { this.panelContainer.addClass(newClass); } else if (toElement && !this.panelEl.hasClass(newClass)) { this.panelEl.addClass(newClass); } }; /** * Remove a class from the panel. DO NOT use this to hide/show the panel. * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * * @param {string} oldClass Class to be removed. * @param {boolean} fromElement Whether or not to remove the class from the * panel element instead of the container. */ MdPanelRef.prototype.removeClass = function(oldClass, fromElement) { this._$log.warn( 'mdPanel: The removeClass method is in the process of being deprecated. ' + 'Full deprecation is scheduled for the AngularJS Material 1.2 release. ' + 'To achieve the same results, use the panelContainer or panelEl ' + 'JQLite elements that are referenced in MdPanelRef.'); if (!this.panelContainer) { throw new Error( 'mdPanel: Panel does not exist yet. Call open() or attach().'); } if (!fromElement && this.panelContainer.hasClass(oldClass)) { this.panelContainer.removeClass(oldClass); } else if (fromElement && this.panelEl.hasClass(oldClass)) { this.panelEl.removeClass(oldClass); } }; /** * Toggle a class on the panel. DO NOT use this to hide/show the panel. * @deprecated * This method is in the process of being deprecated in favor of using the panel * and container JQLite elements that are referenced in the MdPanelRef object. * Full deprecation is scheduled for material 1.2. * * @param {string} toggleClass The class to toggle. * @param {boolean} onElement Whether or not to toggle the class on the panel * element instead of the container. */ MdPanelRef.prototype.toggleClass = function(toggleClass, onElement) { this._$log.warn( 'mdPanel: The toggleClass method is in the process of being deprecated. ' + 'Full deprecation is scheduled for the AngularJS Material 1.2 release. ' + 'To achieve the same results, use the panelContainer or panelEl ' + 'JQLite elements that are referenced in MdPanelRef.'); if (!this.panelContainer) { throw new Error( 'mdPanel: Panel does not exist yet. Call open() or attach().'); } if (!onElement) { this.panelContainer.toggleClass(toggleClass); } else { this.panelEl.toggleClass(toggleClass); } }; /** * Compiles the panel, according to the passed in config and appends it to * the DOM. Helps normalize differences in the compilation process between * using a string template and a content element. * @returns {!angular.$q.Promise} Promise that is resolved when * the element has been compiled and added to the DOM. * @private */ MdPanelRef.prototype._compile = function() { var self = this; // Compile the element via $mdCompiler. Note that when using a // contentElement, the element isn't actually being compiled, rather the // compiler saves it's place in the DOM and provides a way of restoring it. return self._$mdCompiler.compile(self.config).then(function(compileData) { var config = self.config; if (config.contentElement) { var panelEl = compileData.element; // Since mdPanel modifies the inline styles and CSS classes, we need // to save them in order to be able to restore on close. self._restoreCache.styles = panelEl[0].style.cssText; self._restoreCache.classes = panelEl[0].className; self.panelContainer = self._$mdPanel._wrapContentElement(panelEl); self.panelEl = panelEl; } else { self.panelContainer = compileData.link(config['scope']); self.panelEl = angular.element( self.panelContainer[0].querySelector('.md-panel') ); } // Save a reference to the cleanup function from the compiler. self._compilerCleanup = compileData.cleanup; // Attach the panel to the proper place in the DOM. getElement(self.config['attachTo']).append(self.panelContainer); return self; }); }; /** * Creates a panel and adds it to the dom. * @returns {!angular.$q.Promise} A promise that is resolved when the panel is * created. * @private */ MdPanelRef.prototype._createPanel = function() { var self = this; return this._$q(function(resolve, reject) { if (!self.config.locals) { self.config.locals = {}; } self.config.locals.mdPanelRef = self; self._compile().then(function() { if (self.config['disableParentScroll']) { self._restoreScroll = self._$mdUtil.disableScrollAround( null, self.panelContainer, { disableScrollMask: true } ); } // Add a custom CSS class to the panel element. if (self.config['panelClass']) { self.panelEl.addClass(self.config['panelClass']); } // Handle click and touch events for the panel container. if (self.config['propagateContainerEvents']) { self.panelContainer.css('pointer-events', 'none'); self.panelEl.css('pointer-events', 'all'); } // Panel may be outside the $rootElement, tell ngAnimate to animate // regardless. if (self._$animate.pin) { self._$animate.pin( self.panelContainer, getElement(self.config['attachTo']) ); } self._configureTrapFocus(); self._addStyles().then(function() { resolve(self); }, reject); }, reject); }); }; /** * Adds the styles for the panel, such as positioning and z-index. Also, * themes the panel element and panel container using `$mdTheming`. * @returns {!angular.$q.Promise} * @private */ MdPanelRef.prototype._addStyles = function() { var self = this; return this._$q(function(resolve) { self.panelContainer.css('z-index', self.config['zIndex']); self.panelEl.css('z-index', self.config['zIndex'] + 1); var hideAndResolve = function() { // Theme the element and container. self._setTheming(); // Remove offscreen class and add hidden class. self.panelEl.removeClass('_md-panel-offscreen'); self.panelContainer.addClass(MD_PANEL_HIDDEN); resolve(self); }; if (self.config['fullscreen']) { self.panelEl.addClass('_md-panel-fullscreen'); hideAndResolve(); return; // Don't setup positioning. } var positionConfig = self.config['position']; if (!positionConfig) { hideAndResolve(); return; // Don't setup positioning. } // Wait for angular to finish processing the template self._$rootScope['$$postDigest'](function() { // Position it correctly. This is necessary so that the panel will have a // defined height and width. self._updatePosition(true); // Theme the element and container. self._setTheming(); resolve(self); }); }); }; /** * Sets the `$mdTheming` classes on the `panelContainer` and `panelEl`. * @private */ MdPanelRef.prototype._setTheming = function() { this._$mdTheming(this.panelEl); this._$mdTheming(this.panelContainer); }; /** * Updates the position configuration of a panel * @param {!MdPanelPosition} position */ MdPanelRef.prototype.updatePosition = function(position) { if (!this.panelContainer) { throw new Error( 'mdPanel: Panel does not exist yet. Call open() or attach().'); } this.config['position'] = position; this._updatePosition(); }; /** * Calculates and updates the position of the panel. * @param {boolean=} init * @private */ MdPanelRef.prototype._updatePosition = function(init) { var positionConfig = this.config['position']; if (positionConfig) { positionConfig._setPanelPosition(this.panelEl); // Hide the panel now that position is known. if (init) { this.panelEl.removeClass('_md-panel-offscreen'); this.panelContainer.addClass(MD_PANEL_HIDDEN); } this.panelEl.css( MdPanelPosition.absPosition.TOP, positionConfig.getTop() ); this.panelEl.css( MdPanelPosition.absPosition.BOTTOM, positionConfig.getBottom() ); this.panelEl.css( MdPanelPosition.absPosition.LEFT, positionConfig.getLeft() ); this.panelEl.css( MdPanelPosition.absPosition.RIGHT, positionConfig.getRight() ); } }; /** * Focuses on the panel or the first focus target. * @private */ MdPanelRef.prototype._focusOnOpen = function() { if (this.config['focusOnOpen']) { // Wait for the template to finish rendering to guarantee md-autofocus has // finished adding the class md-autofocus, otherwise the focusable element // isn't available to focus. var self = this; this._$rootScope['$$postDigest'](function() { var target = self._$mdUtil.findFocusTarget(self.panelEl) || self.panelEl; target.focus(); }); } }; /** * Shows the backdrop. * @returns {!angular.$q.Promise} A promise that is resolved when the backdrop * is created and attached. * @private */ MdPanelRef.prototype._createBackdrop = function() { if (this.config.hasBackdrop) { if (!this._backdropRef) { var backdropAnimation = this._$mdPanel.newPanelAnimation() .openFrom(this.config.attachTo) .withAnimation({ open: '_md-opaque-enter', close: '_md-opaque-leave' }); if (this.config.animation) { backdropAnimation.duration(this.config.animation._rawDuration); } var backdropConfig = { animation: backdropAnimation, attachTo: this.config.attachTo, focusOnOpen: false, panelClass: '_md-panel-backdrop', zIndex: this.config.zIndex - 1 }; this._backdropRef = this._$mdPanel.create(backdropConfig); } if (!this._backdropRef.isAttached) { return this._backdropRef.attach(); } } }; /** * Listen for escape keys and outside clicks to auto close. * @private */ MdPanelRef.prototype._addEventListeners = function() { this._configureEscapeToClose(); this._configureClickOutsideToClose(); this._configureScrollListener(); }; /** * Remove event listeners added in _addEventListeners. * @private */ MdPanelRef.prototype._removeEventListeners = function() { this._removeListeners && this._removeListeners.forEach(function(removeFn) { removeFn(); }); this._removeListeners = []; }; /** * Setup the escapeToClose event listeners. * @private */ MdPanelRef.prototype._configureEscapeToClose = function() { if (this.config['escapeToClose']) { var parentTarget = getElement(this.config['attachTo']); var self = this; var keyHandlerFn = function(ev) { if (ev.keyCode === self._$mdConstant.KEY_CODE.ESCAPE) { ev.stopPropagation(); ev.preventDefault(); self.close(MdPanelRef.closeReasons.ESCAPE); } }; // Add keydown listeners this.panelContainer.on('keydown', keyHandlerFn); parentTarget.on('keydown', keyHandlerFn); // Queue remove listeners function this._removeListeners.push(function() { self.panelContainer.off('keydown', keyHandlerFn); parentTarget.off('keydown', keyHandlerFn); }); } }; /** * Setup the clickOutsideToClose event listeners. * @private */ MdPanelRef.prototype._configureClickOutsideToClose = function() { if (this.config['clickOutsideToClose']) { var target = this.config['propagateContainerEvents'] ? angular.element(document.body) : this.panelContainer; var sourceEl; // Keep track of the element on which the mouse originally went down // so that we can only close the backdrop when the 'click' started on it. // A simple 'click' handler does not work, it sets the target object as the // element the mouse went down on. var mousedownHandler = function(ev) { sourceEl = ev.target; }; // We check if our original element and the target is the backdrop // because if the original was the backdrop and the target was inside the // panel we don't want to panel to close. var self = this; var mouseupHandler = function(ev) { if (self.config['propagateContainerEvents']) { // We check if the sourceEl of the event is the panel element or one // of it's children. If it is not, then close the panel. if (sourceEl !== self.panelEl[0] && !self.panelEl[0].contains(sourceEl)) { self.close(); } } else if (sourceEl === target[0] && ev.target === target[0]) { ev.stopPropagation(); ev.preventDefault(); self.close(MdPanelRef.closeReasons.CLICK_OUTSIDE); } }; // Add listeners target.on('mousedown', mousedownHandler); target.on('mouseup', mouseupHandler); // Queue remove listeners function this._removeListeners.push(function() { target.off('mousedown', mousedownHandler); target.off('mouseup', mouseupHandler); }); } }; /** * Configures the listeners for updating the panel position on scroll. * @private */ MdPanelRef.prototype._configureScrollListener = function() { // No need to bind the event if scrolling is disabled. if (!this.config['disableParentScroll']) { var updatePosition = angular.bind(this, this._updatePosition); var debouncedUpdatePosition = this._$$rAF.throttle(updatePosition); var self = this; var onScroll = function() { debouncedUpdatePosition(); }; // Add listeners. this._$window.addEventListener('scroll', onScroll, true); // Queue remove listeners function. this._removeListeners.push(function() { self._$window.removeEventListener('scroll', onScroll, true); }); } }; /** * Setup the focus traps. These traps will wrap focus when tabbing past the * panel. When shift-tabbing, the focus will stick in place. * @private */ MdPanelRef.prototype._configureTrapFocus = function() { // Focus doesn't remain inside of the panel without this. this.panelEl.attr('tabIndex', '-1'); if (this.config['trapFocus']) { var element = this.panelEl; // Set up elements before and after the panel to capture focus and // redirect back into the panel. this._topFocusTrap = FOCUS_TRAP_TEMPLATE.clone()[0]; this._bottomFocusTrap = FOCUS_TRAP_TEMPLATE.clone()[0]; // When focus is about to move out of the panel, we want to intercept it // and redirect it back to the panel element. var focusHandler = function() { element.focus(); }; this._topFocusTrap.addEventListener('focus', focusHandler); this._bottomFocusTrap.addEventListener('focus', focusHandler); // Queue remove listeners function this._removeListeners.push(this._simpleBind(function() { this._topFocusTrap.removeEventListener('focus', focusHandler); this._bottomFocusTrap.removeEventListener('focus', focusHandler); }, this)); // The top focus trap inserted immediately before the md-panel element (as // a sibling). The bottom focus trap inserted immediately after the // md-panel element (as a sibling). element[0].parentNode.insertBefore(this._topFocusTrap, element[0]); element.after(this._bottomFocusTrap); } }; /** * Updates the animation of a panel. * @param {!MdPanelAnimation} animation */ MdPanelRef.prototype.updateAnimation = function(animation) { this.config['animation'] = animation; if (this._backdropRef) { this._backdropRef.config.animation.duration(animation._rawDuration); } }; /** * Animate the panel opening. * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * animated open. * @private */ MdPanelRef.prototype._animateOpen = function() { this.panelContainer.addClass('md-panel-is-showing'); var animationConfig = this.config['animation']; if (!animationConfig) { // Promise is in progress, return it. this.panelContainer.addClass('_md-panel-shown'); return this._$q.when(this); } var self = this; return this._$q(function(resolve) { var done = self._done(resolve, self); var warnAndOpen = function() { self._$log.warn( 'mdPanel: MdPanel Animations failed. ' + 'Showing panel without animating.'); done(); }; animationConfig.animateOpen(self.panelEl) .then(done, warnAndOpen); }); }; /** * Animate the panel closing. * @returns {!angular.$q.Promise} A promise that is resolved when the panel has * animated closed. * @private */ MdPanelRef.prototype._animateClose = function() { var animationConfig = this.config['animation']; if (!animationConfig) { this.panelContainer.removeClass('md-panel-is-showing'); this.panelContainer.removeClass('_md-panel-shown'); return this._$q.when(this); } var self = this; return this._$q(function(resolve) { var done = function() { self.panelContainer.removeClass('md-panel-is-showing'); resolve(self); }; var warnAndClose = function() { self._$log.warn( 'mdPanel: MdPanel Animations failed. ' + 'Hiding panel without animating.'); done(); }; animationConfig.animateClose(self.panelEl) .then(done, warnAndClose); }); }; /** * Registers a interceptor with the panel. The callback should return a promise, * which will allow the action to continue when it gets resolved, or will * prevent an action if it is rejected. * @param {string} type Type of interceptor. * @param {!angular.$q.Promise} callback Callback to be registered. * @returns {!MdPanelRef} */ MdPanelRef.prototype.registerInterceptor = function(type, callback) { var error = null; if (!angular.isString(type)) { error = 'Interceptor type must be a string, instead got ' + typeof type; } else if (!angular.isFunction(callback)) { error = 'Interceptor callback must be a function, instead got ' + typeof callback; } if (error) { throw new Error('MdPanel: ' + error); } var interceptors = this._interceptors[type] = this._interceptors[type] || []; if (interceptors.indexOf(callback) === -1) { interceptors.push(callback); } return this; }; /** * Removes a registered interceptor. * @param {string} type Type of interceptor to be removed. * @param {Function} callback Interceptor to be removed. * @returns {!MdPanelRef} */ MdPanelRef.prototype.removeInterceptor = function(type, callback) { var index = this._interceptors[type] ? this._interceptors[type].indexOf(callback) : -1; if (index > -1) { this._interceptors[type].splice(index, 1); } return this; }; /** * Removes all interceptors. * @param {string=} type Type of interceptors to be removed. * If ommited, all interceptors types will be removed. * @returns {!MdPanelRef} */ MdPanelRef.prototype.removeAllInterceptors = function(type) { if (type) { this._interceptors[type] = []; } else { this._interceptors = Object.create(null); } return this; }; /** * Invokes all the interceptors of a certain type sequantially in * reverse order. Works in a similar way to `$q.all`, except it * respects the order of the functions. * @param {string} type Type of interceptors to be invoked. * @returns {!angular.$q.Promise} * @private */ MdPanelRef.prototype._callInterceptors = function(type) { var self = this; var $q = self._$q; var interceptors = self._interceptors && self._interceptors[type] || []; return interceptors.reduceRight(function(promise, interceptor) { var isPromiseLike = interceptor && angular.isFunction(interceptor.then); var response = isPromiseLike ? interceptor : null; /** * For interceptors to reject/cancel subsequent portions of the chain, simply * return a `$q.reject()` */ return promise.then(function() { if (!response) { try { response = interceptor(self); } catch(e) { response = $q.reject(e); } } return response; }); }, $q.resolve(self)); }; /** * Faster, more basic than angular.bind * http://jsperf.com/angular-bind-vs-custom-vs-native * @param {function} callback * @param {!Object} self * @return {function} Callback function with a bound self. */ MdPanelRef.prototype._simpleBind = function(callback, self) { return function(value) { return callback.apply(self, value); }; }; /** * @param {function} callback * @param {!Object} self * @return {function} Callback function with a self param. */ MdPanelRef.prototype._done = function(callback, self) { return function() { callback(self); }; }; /** * Adds a panel to a group if the panel does not exist within the group already. * A panel can only exist within a single group. * @param {string} groupName The name of the group. */ MdPanelRef.prototype.addToGroup = function(groupName) { if (!this._$mdPanel._groups[groupName]) { this._$mdPanel.newPanelGroup(groupName); } var group = this._$mdPanel._groups[groupName]; var index = group.panels.indexOf(this); if (index < 0) { group.panels.push(this); } }; /** * Removes a panel from a group if the panel exists within that group. The group * must be created ahead of time. * @param {string} groupName The name of the group. */ MdPanelRef.prototype.removeFromGroup = function(groupName) { if (!this._$mdPanel._groups[groupName]) { throw new Error('mdPanel: The group ' + groupName + ' does not exist.'); } var group = this._$mdPanel._groups[groupName]; var index = group.panels.indexOf(this); if (index > -1) { group.panels.splice(index, 1); } }; /** * Possible default closeReasons for the close function. * @enum {string} */ MdPanelRef.closeReasons = { CLICK_OUTSIDE: 'clickOutsideToClose', ESCAPE: 'escapeToClose', }; /***************************************************************************** * MdPanelPosition * *****************************************************************************/ /** * Position configuration object. To use, create an MdPanelPosition with the * desired properties, then pass the object as part of $mdPanel creation. * * Example: * * var panelPosition = new MdPanelPosition() * .relativeTo(myButtonEl) * .addPanelPosition( * $mdPanel.xPosition.CENTER, * $mdPanel.yPosition.ALIGN_TOPS * ); * * $mdPanel.create({ * position: panelPosition * }); * * @param {!angular.$injector} $injector * @final @constructor */ function MdPanelPosition($injector) { /** @private @const {!angular.$window} */ this._$window = $injector.get('$window'); /** @private {boolean} */ this._isRTL = $injector.get('$mdUtil').bidi() === 'rtl'; /** @private @const {!angular.$mdConstant} */ this._$mdConstant = $injector.get('$mdConstant'); /** @private {boolean} */ this._absolute = false; /** @private {!angular.JQLite} */ this._relativeToEl; /** @private {string} */ this._top = ''; /** @private {string} */ this._bottom = ''; /** @private {string} */ this._left = ''; /** @private {string} */ this._right = ''; /** @private {!Array} */ this._translateX = []; /** @private {!Array} */ this._translateY = []; /** @private {!Array<{x:string, y:string}>} */ this._positions = []; /** @private {?{x:string, y:string}} */ this._actualPosition; } /** * Possible values of xPosition. * @enum {string} */ MdPanelPosition.xPosition = { CENTER: 'center', ALIGN_START: 'align-start', ALIGN_END: 'align-end', OFFSET_START: 'offset-start', OFFSET_END: 'offset-end' }; /** * Possible values of yPosition. * @enum {string} */ MdPanelPosition.yPosition = { CENTER: 'center', ALIGN_TOPS: 'align-tops', ALIGN_BOTTOMS: 'align-bottoms', ABOVE: 'above', BELOW: 'below' }; /** * Possible values of absolute position. * @enum {string} */ MdPanelPosition.absPosition = { TOP: 'top', RIGHT: 'right', BOTTOM: 'bottom', LEFT: 'left' }; /** * Margin between the edges of a panel and the viewport. * @const {number} */ MdPanelPosition.viewportMargin = 8; /** * Sets absolute positioning for the panel. * @return {!MdPanelPosition} */ MdPanelPosition.prototype.absolute = function() { this._absolute = true; return this; }; /** * Sets the value of a position for the panel. Clears any previously set * position. * @param {string} position Position to set * @param {string=} value Value of the position. Defaults to '0'. * @returns {!MdPanelPosition} * @private */ MdPanelPosition.prototype._setPosition = function(position, value) { if (position === MdPanelPosition.absPosition.RIGHT || position === MdPanelPosition.absPosition.LEFT) { this._left = this._right = ''; } else if ( position === MdPanelPosition.absPosition.BOTTOM || position === MdPanelPosition.absPosition.TOP) { this._top = this._bottom = ''; } else { var positions = Object.keys(MdPanelPosition.absPosition).join() .toLowerCase(); throw new Error('mdPanel: Position must be one of ' + positions + '.'); } this['_' + position] = angular.isString(value) ? value : '0'; return this; }; /** * Sets the value of `top` for the panel. Clears any previously set vertical * position. * @param {string=} top Value of `top`. Defaults to '0'. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.top = function(top) { return this._setPosition(MdPanelPosition.absPosition.TOP, top); }; /** * Sets the value of `bottom` for the panel. Clears any previously set vertical * position. * @param {string=} bottom Value of `bottom`. Defaults to '0'. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.bottom = function(bottom) { return this._setPosition(MdPanelPosition.absPosition.BOTTOM, bottom); }; /** * Sets the panel to the start of the page - `left` if `ltr` or `right` for * `rtl`. Clears any previously set horizontal position. * @param {string=} start Value of position. Defaults to '0'. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.start = function(start) { var position = this._isRTL ? MdPanelPosition.absPosition.RIGHT : MdPanelPosition.absPosition.LEFT; return this._setPosition(position, start); }; /** * Sets the panel to the end of the page - `right` if `ltr` or `left` for `rtl`. * Clears any previously set horizontal position. * @param {string=} end Value of position. Defaults to '0'. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.end = function(end) { var position = this._isRTL ? MdPanelPosition.absPosition.LEFT : MdPanelPosition.absPosition.RIGHT; return this._setPosition(position, end); }; /** * Sets the value of `left` for the panel. Clears any previously set * horizontal position. * @param {string=} left Value of `left`. Defaults to '0'. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.left = function(left) { return this._setPosition(MdPanelPosition.absPosition.LEFT, left); }; /** * Sets the value of `right` for the panel. Clears any previously set * horizontal position. * @param {string=} right Value of `right`. Defaults to '0'. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.right = function(right) { return this._setPosition(MdPanelPosition.absPosition.RIGHT, right); }; /** * Centers the panel horizontally in the viewport. Clears any previously set * horizontal position. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.centerHorizontally = function() { this._left = '50%'; this._right = ''; this._translateX = ['-50%']; return this; }; /** * Centers the panel vertically in the viewport. Clears any previously set * vertical position. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.centerVertically = function() { this._top = '50%'; this._bottom = ''; this._translateY = ['-50%']; return this; }; /** * Centers the panel horizontally and vertically in the viewport. This is * equivalent to calling both `centerHorizontally` and `centerVertically`. * Clears any previously set horizontal and vertical positions. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.center = function() { return this.centerHorizontally().centerVertically(); }; /** * Sets element for relative positioning. * @param {string|!Element|!angular.JQLite} element Query selector, DOM element, * or angular element to set the panel relative to. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.relativeTo = function(element) { this._absolute = false; this._relativeToEl = getElement(element); return this; }; /** * Sets the x and y positions for the panel relative to another element. * @param {string} xPosition must be one of the MdPanelPosition.xPosition * values. * @param {string} yPosition must be one of the MdPanelPosition.yPosition * values. * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.addPanelPosition = function(xPosition, yPosition) { if (!this._relativeToEl) { throw new Error('mdPanel: addPanelPosition can only be used with ' + 'relative positioning. Set relativeTo first.'); } this._validateXPosition(xPosition); this._validateYPosition(yPosition); this._positions.push({ x: xPosition, y: yPosition, }); return this; }; /** * Ensures that yPosition is a valid position name. Throw an exception if not. * @param {string} yPosition */ MdPanelPosition.prototype._validateYPosition = function(yPosition) { // empty is ok if (yPosition == null) { return; } var positionKeys = Object.keys(MdPanelPosition.yPosition); var positionValues = []; for (var key, i = 0; key = positionKeys[i]; i++) { var position = MdPanelPosition.yPosition[key]; positionValues.push(position); if (position === yPosition) { return; } } throw new Error('mdPanel: Panel y position only accepts the following ' + 'values:\n' + positionValues.join(' | ')); }; /** * Ensures that xPosition is a valid position name. Throw an exception if not. * @param {string} xPosition */ MdPanelPosition.prototype._validateXPosition = function(xPosition) { // empty is ok if (xPosition == null) { return; } var positionKeys = Object.keys(MdPanelPosition.xPosition); var positionValues = []; for (var key, i = 0; key = positionKeys[i]; i++) { var position = MdPanelPosition.xPosition[key]; positionValues.push(position); if (position === xPosition) { return; } } throw new Error('mdPanel: Panel x Position only accepts the following ' + 'values:\n' + positionValues.join(' | ')); }; /** * Sets the value of the offset in the x-direction. This will add to any * previously set offsets. * @param {string|function(MdPanelPosition): string} offsetX * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.withOffsetX = function(offsetX) { this._translateX.push(offsetX); return this; }; /** * Sets the value of the offset in the y-direction. This will add to any * previously set offsets. * @param {string|function(MdPanelPosition): string} offsetY * @returns {!MdPanelPosition} */ MdPanelPosition.prototype.withOffsetY = function(offsetY) { this._translateY.push(offsetY); return this; }; /** * Gets the value of `top` for the panel. * @returns {string} */ MdPanelPosition.prototype.getTop = function() { return this._top; }; /** * Gets the value of `bottom` for the panel. * @returns {string} */ MdPanelPosition.prototype.getBottom = function() { return this._bottom; }; /** * Gets the value of `left` for the panel. * @returns {string} */ MdPanelPosition.prototype.getLeft = function() { return this._left; }; /** * Gets the value of `right` for the panel. * @returns {string} */ MdPanelPosition.prototype.getRight = function() { return this._right; }; /** * Gets the value of `transform` for the panel. * @returns {string} */ MdPanelPosition.prototype.getTransform = function() { var translateX = this._reduceTranslateValues('translateX', this._translateX); var translateY = this._reduceTranslateValues('translateY', this._translateY); // It's important to trim the result, because the browser will ignore the set // operation if the string contains only whitespace. return (translateX + ' ' + translateY).trim(); }; /** * Sets the `transform` value for a panel element. * @param {!angular.JQLite} panelEl * @returns {!angular.JQLite} * @private */ MdPanelPosition.prototype._setTransform = function(panelEl) { return panelEl.css(this._$mdConstant.CSS.TRANSFORM, this.getTransform()); }; /** * True if the panel is completely on-screen with this positioning; false * otherwise. * @param {!angular.JQLite} panelEl * @return {boolean} * @private */ MdPanelPosition.prototype._isOnscreen = function(panelEl) { // this works because we always use fixed positioning for the panel, // which is relative to the viewport. var left = parseInt(this.getLeft()); var top = parseInt(this.getTop()); if (this._translateX.length || this._translateY.length) { var prefixedTransform = this._$mdConstant.CSS.TRANSFORM; var offsets = getComputedTranslations(panelEl, prefixedTransform); left += offsets.x; top += offsets.y; } var right = left + panelEl[0].offsetWidth; var bottom = top + panelEl[0].offsetHeight; return (left >= 0) && (top >= 0) && (bottom <= this._$window.innerHeight) && (right <= this._$window.innerWidth); }; /** * Gets the first x/y position that can fit on-screen. * @returns {{x: string, y: string}} */ MdPanelPosition.prototype.getActualPosition = function() { return this._actualPosition; }; /** * Reduces a list of translate values to a string that can be used within * transform. * @param {string} translateFn * @param {!Array} values * @returns {string} * @private */ MdPanelPosition.prototype._reduceTranslateValues = function(translateFn, values) { return values.map(function(translation) { // TODO(crisbeto): this should add the units after #9609 is merged. var translationValue = angular.isFunction(translation) ? translation(this) : translation; return translateFn + '(' + translationValue + ')'; }, this).join(' '); }; /** * Sets the panel position based on the created panel element and best x/y * positioning. * @param {!angular.JQLite} panelEl * @private */ MdPanelPosition.prototype._setPanelPosition = function(panelEl) { // Remove the "position adjusted" class in case it has been added before. panelEl.removeClass('_md-panel-position-adjusted'); // Only calculate the position if necessary. if (this._absolute) { this._setTransform(panelEl); return; } if (this._actualPosition) { this._calculatePanelPosition(panelEl, this._actualPosition); this._setTransform(panelEl); this._constrainToViewport(panelEl); return; } for (var i = 0; i < this._positions.length; i++) { this._actualPosition = this._positions[i]; this._calculatePanelPosition(panelEl, this._actualPosition); this._setTransform(panelEl); if (this._isOnscreen(panelEl)) { return; } } this._constrainToViewport(panelEl); }; /** * Constrains a panel's position to the viewport. * @param {!angular.JQLite} panelEl * @private */ MdPanelPosition.prototype._constrainToViewport = function(panelEl) { var margin = MdPanelPosition.viewportMargin; var initialTop = this._top; var initialLeft = this._left; if (this.getTop()) { var top = parseInt(this.getTop()); var bottom = panelEl[0].offsetHeight + top; var viewportHeight = this._$window.innerHeight; if (top < margin) { this._top = margin + 'px'; } else if (bottom > viewportHeight) { this._top = top - (bottom - viewportHeight + margin) + 'px'; } } if (this.getLeft()) { var left = parseInt(this.getLeft()); var right = panelEl[0].offsetWidth + left; var viewportWidth = this._$window.innerWidth; if (left < margin) { this._left = margin + 'px'; } else if (right > viewportWidth) { this._left = left - (right - viewportWidth + margin) + 'px'; } } // Class that can be used to re-style the panel if it was repositioned. panelEl.toggleClass( '_md-panel-position-adjusted', this._top !== initialTop || this._left !== initialLeft ); }; /** * Switches between 'start' and 'end'. * @param {string} position Horizontal position of the panel * @returns {string} Reversed position * @private */ MdPanelPosition.prototype._reverseXPosition = function(position) { if (position === MdPanelPosition.xPosition.CENTER) { return position; } var start = 'start'; var end = 'end'; return position.indexOf(start) > -1 ? position.replace(start, end) : position.replace(end, start); }; /** * Handles horizontal positioning in rtl or ltr environments. * @param {string} position Horizontal position of the panel * @returns {string} The correct position according the page direction * @private */ MdPanelPosition.prototype._bidi = function(position) { return this._isRTL ? this._reverseXPosition(position) : position; }; /** * Calculates the panel position based on the created panel element and the * provided positioning. * @param {!angular.JQLite} panelEl * @param {!{x:string, y:string}} position * @private */ MdPanelPosition.prototype._calculatePanelPosition = function(panelEl, position) { var panelBounds = panelEl[0].getBoundingClientRect(); var panelWidth = panelBounds.width; var panelHeight = panelBounds.height; var targetBounds = this._relativeToEl[0].getBoundingClientRect(); var targetLeft = targetBounds.left; var targetRight = targetBounds.right; var targetWidth = targetBounds.width; switch (this._bidi(position.x)) { case MdPanelPosition.xPosition.OFFSET_START: this._left = targetLeft - panelWidth + 'px'; break; case MdPanelPosition.xPosition.ALIGN_END: this._left = targetRight - panelWidth + 'px'; break; case MdPanelPosition.xPosition.CENTER: var left = targetLeft + (0.5 * targetWidth) - (0.5 * panelWidth); this._left = left + 'px'; break; case MdPanelPosition.xPosition.ALIGN_START: this._left = targetLeft + 'px'; break; case MdPanelPosition.xPosition.OFFSET_END: this._left = targetRight + 'px'; break; } var targetTop = targetBounds.top; var targetBottom = targetBounds.bottom; var targetHeight = targetBounds.height; switch (position.y) { case MdPanelPosition.yPosition.ABOVE: this._top = targetTop - panelHeight + 'px'; break; case MdPanelPosition.yPosition.ALIGN_BOTTOMS: this._top = targetBottom - panelHeight + 'px'; break; case MdPanelPosition.yPosition.CENTER: var top = targetTop + (0.5 * targetHeight) - (0.5 * panelHeight); this._top = top + 'px'; break; case MdPanelPosition.yPosition.ALIGN_TOPS: this._top = targetTop + 'px'; break; case MdPanelPosition.yPosition.BELOW: this._top = targetBottom + 'px'; break; } }; /***************************************************************************** * MdPanelAnimation * *****************************************************************************/ /** * Animation configuration object. To use, create an MdPanelAnimation with the * desired properties, then pass the object as part of $mdPanel creation. * * Example: * * var panelAnimation = new MdPanelAnimation() * .openFrom(myButtonEl) * .closeTo('.my-button') * .withAnimation($mdPanel.animation.SCALE); * * $mdPanel.create({ * animation: panelAnimation * }); * * @param {!angular.$injector} $injector * @final @constructor */ function MdPanelAnimation($injector) { /** @private @const {!angular.$mdUtil} */ this._$mdUtil = $injector.get('$mdUtil'); /** * @private {{element: !angular.JQLite|undefined, bounds: !DOMRect}| * undefined} */ this._openFrom; /** * @private {{element: !angular.JQLite|undefined, bounds: !DOMRect}| * undefined} */ this._closeTo; /** @private {string|{open: string, close: string}} */ this._animationClass = ''; /** @private {number} */ this._openDuration; /** @private {number} */ this._closeDuration; /** @private {number|{open: number, close: number}} */ this._rawDuration; } /** * Possible default animations. * @enum {string} */ MdPanelAnimation.animation = { SLIDE: 'md-panel-animate-slide', SCALE: 'md-panel-animate-scale', FADE: 'md-panel-animate-fade' }; /** * Specifies where to start the open animation. `openFrom` accepts a * click event object, query selector, DOM element, or a Rect object that * is used to determine the bounds. When passed a click event, the location * of the click will be used as the position to start the animation. * @param {string|!Element|!Event|{top: number, left: number}} openFrom * @returns {!MdPanelAnimation} */ MdPanelAnimation.prototype.openFrom = function(openFrom) { // Check if 'openFrom' is an Event. openFrom = openFrom.target ? openFrom.target : openFrom; this._openFrom = this._getPanelAnimationTarget(openFrom); if (!this._closeTo) { this._closeTo = this._openFrom; } return this; }; /** * Specifies where to animate the panel close. `closeTo` accepts a * query selector, DOM element, or a Rect object that is used to determine * the bounds. * @param {string|!Element|{top: number, left: number}} closeTo * @returns {!MdPanelAnimation} */ MdPanelAnimation.prototype.closeTo = function(closeTo) { this._closeTo = this._getPanelAnimationTarget(closeTo); return this; }; /** * Specifies the duration of the animation in milliseconds. * @param {number|{open: number, close: number}} duration * @returns {!MdPanelAnimation} */ MdPanelAnimation.prototype.duration = function(duration) { if (duration) { if (angular.isNumber(duration)) { this._openDuration = this._closeDuration = toSeconds(duration); } else if (angular.isObject(duration)) { this._openDuration = toSeconds(duration.open); this._closeDuration = toSeconds(duration.close); } } // Save the original value so it can be passed to the backdrop. this._rawDuration = duration; return this; function toSeconds(value) { if (angular.isNumber(value)) return value / 1000; } }; /** * Returns the element and bounds for the animation target. * @param {string|!Element|{top: number, left: number}} location * @returns {{element: !angular.JQLite|undefined, bounds: !DOMRect}} * @private */ MdPanelAnimation.prototype._getPanelAnimationTarget = function(location) { if (angular.isDefined(location.top) || angular.isDefined(location.left)) { return { element: undefined, bounds: { top: location.top || 0, left: location.left || 0 } }; } else { return this._getBoundingClientRect(getElement(location)); } }; /** * Specifies the animation class. * * There are several default animations that can be used: * (MdPanelAnimation.animation) * SLIDE: The panel slides in and out from the specified * elements. * SCALE: The panel scales in and out. * FADE: The panel fades in and out. * * @param {string|{open: string, close: string}} cssClass * @returns {!MdPanelAnimation} */ MdPanelAnimation.prototype.withAnimation = function(cssClass) { this._animationClass = cssClass; return this; }; /** * Animate the panel open. * @param {!angular.JQLite} panelEl * @returns {!angular.$q.Promise} A promise that is resolved when the open * animation is complete. */ MdPanelAnimation.prototype.animateOpen = function(panelEl) { var animator = this._$mdUtil.dom.animator; this._fixBounds(panelEl); var animationOptions = {}; // Include the panel transformations when calculating the animations. var panelTransform = panelEl[0].style.transform || ''; var openFrom = animator.toTransformCss(panelTransform); var openTo = animator.toTransformCss(panelTransform); switch (this._animationClass) { case MdPanelAnimation.animation.SLIDE: // Slide should start with opacity: 1. panelEl.css('opacity', '1'); animationOptions = { transitionInClass: '_md-panel-animate-enter' }; var openSlide = animator.calculateSlideToOrigin( panelEl, this._openFrom) || ''; openFrom = animator.toTransformCss(openSlide + ' ' + panelTransform); break; case MdPanelAnimation.animation.SCALE: animationOptions = { transitionInClass: '_md-panel-animate-enter' }; var openScale = animator.calculateZoomToOrigin( panelEl, this._openFrom) || ''; openFrom = animator.toTransformCss(openScale + ' ' + panelTransform); break; case MdPanelAnimation.animation.FADE: animationOptions = { transitionInClass: '_md-panel-animate-enter' }; break; default: if (angular.isString(this._animationClass)) { animationOptions = { transitionInClass: this._animationClass }; } else { animationOptions = { transitionInClass: this._animationClass['open'], transitionOutClass: this._animationClass['close'], }; } } animationOptions.duration = this._openDuration; return animator .translate3d(panelEl, openFrom, openTo, animationOptions); }; /** * Animate the panel close. * @param {!angular.JQLite} panelEl * @returns {!angular.$q.Promise} A promise that resolves when the close * animation is complete. */ MdPanelAnimation.prototype.animateClose = function(panelEl) { var animator = this._$mdUtil.dom.animator; var reverseAnimationOptions = {}; // Include the panel transformations when calculating the animations. var panelTransform = panelEl[0].style.transform || ''; var closeFrom = animator.toTransformCss(panelTransform); var closeTo = animator.toTransformCss(panelTransform); switch (this._animationClass) { case MdPanelAnimation.animation.SLIDE: // Slide should start with opacity: 1. panelEl.css('opacity', '1'); reverseAnimationOptions = { transitionInClass: '_md-panel-animate-leave' }; var closeSlide = animator.calculateSlideToOrigin( panelEl, this._closeTo) || ''; closeTo = animator.toTransformCss(closeSlide + ' ' + panelTransform); break; case MdPanelAnimation.animation.SCALE: reverseAnimationOptions = { transitionInClass: '_md-panel-animate-scale-out _md-panel-animate-leave' }; var closeScale = animator.calculateZoomToOrigin( panelEl, this._closeTo) || ''; closeTo = animator.toTransformCss(closeScale + ' ' + panelTransform); break; case MdPanelAnimation.animation.FADE: reverseAnimationOptions = { transitionInClass: '_md-panel-animate-fade-out _md-panel-animate-leave' }; break; default: if (angular.isString(this._animationClass)) { reverseAnimationOptions = { transitionOutClass: this._animationClass }; } else { reverseAnimationOptions = { transitionInClass: this._animationClass['close'], transitionOutClass: this._animationClass['open'] }; } } reverseAnimationOptions.duration = this._closeDuration; return animator .translate3d(panelEl, closeFrom, closeTo, reverseAnimationOptions); }; /** * Set the height and width to match the panel if not provided. * @param {!angular.JQLite} panelEl * @private */ MdPanelAnimation.prototype._fixBounds = function(panelEl) { var panelWidth = panelEl[0].offsetWidth; var panelHeight = panelEl[0].offsetHeight; if (this._openFrom && this._openFrom.bounds.height == null) { this._openFrom.bounds.height = panelHeight; } if (this._openFrom && this._openFrom.bounds.width == null) { this._openFrom.bounds.width = panelWidth; } if (this._closeTo && this._closeTo.bounds.height == null) { this._closeTo.bounds.height = panelHeight; } if (this._closeTo && this._closeTo.bounds.width == null) { this._closeTo.bounds.width = panelWidth; } }; /** * Identify the bounding RECT for the target element. * @param {!angular.JQLite} element * @returns {{element: !angular.JQLite|undefined, bounds: !DOMRect}} * @private */ MdPanelAnimation.prototype._getBoundingClientRect = function(element) { if (element instanceof angular.element) { return { element: element, bounds: element[0].getBoundingClientRect() }; } }; /***************************************************************************** * Util Methods * *****************************************************************************/ /** * Returns the angular element associated with a css selector or element. * @param el {string|!angular.JQLite|!Element} * @returns {!angular.JQLite} */ function getElement(el) { var queryResult = angular.isString(el) ? document.querySelector(el) : el; return angular.element(queryResult); } /** * Gets the computed values for an element's translateX and translateY in px. * @param {!angular.JQLite|!Element} el * @param {string} property * @return {{x: number, y: number}} */ function getComputedTranslations(el, property) { // The transform being returned by `getComputedStyle` is in the format: // `matrix(a, b, c, d, translateX, translateY)` if defined and `none` // if the element doesn't have a transform. var transform = getComputedStyle(el[0] || el)[property]; var openIndex = transform.indexOf('('); var closeIndex = transform.lastIndexOf(')'); var output = { x: 0, y: 0 }; if (openIndex > -1 && closeIndex > -1) { var parsedValues = transform .substring(openIndex + 1, closeIndex) .split(', ') .slice(-2); output.x = parseInt(parsedValues[0]); output.y = parseInt(parsedValues[1]); } return output; } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.progressCircular * @description Module for a circular progressbar */ angular.module('material.components.progressCircular', ['material.core']); })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.progressLinear * @description Linear Progress module! */ MdProgressLinearDirective.$inject = ["$mdTheming", "$mdUtil", "$log"]; angular.module('material.components.progressLinear', [ 'material.core' ]) .directive('mdProgressLinear', MdProgressLinearDirective); /** * @ngdoc directive * @name mdProgressLinear * @module material.components.progressLinear * @restrict E * * @description * The linear progress directive is used to make loading content * in your app as delightful and painless as possible by minimizing * the amount of visual change a user sees before they can view * and interact with content. * * Each operation should only be represented by one activity indicator * For example: one refresh operation should not display both a * refresh bar and an activity circle. * * For operations where the percentage of the operation completed * can be determined, use a determinate indicator. They give users * a quick sense of how long an operation will take. * * For operations where the user is asked to wait a moment while * something finishes up, and it’s not necessary to expose what's * happening behind the scenes and how long it will take, use an * indeterminate indicator. * * @param {string} md-mode Select from one of four modes: determinate, indeterminate, buffer or query. * * Note: if the `md-mode` value is set as undefined or specified as 1 of the four (4) valid modes, then `indeterminate` * will be auto-applied as the mode. * * Note: if not configured, the `md-mode="indeterminate"` will be auto injected as an attribute. If `value=""` is also specified, however, * then `md-mode="determinate"` would be auto-injected instead. * @param {number=} value In determinate and buffer modes, this number represents the percentage of the primary progress bar. Default: 0 * @param {number=} md-buffer-value In the buffer mode, this number represents the percentage of the secondary progress bar. Default: 0 * @param {boolean=} ng-disabled Determines whether to disable the progress element. * * @usage * * * * * * * * * * * */ function MdProgressLinearDirective($mdTheming, $mdUtil, $log) { var MODE_DETERMINATE = "determinate"; var MODE_INDETERMINATE = "indeterminate"; var MODE_BUFFER = "buffer"; var MODE_QUERY = "query"; var DISABLED_CLASS = "_md-progress-linear-disabled"; return { restrict: 'E', template: '
    ' + '
    ' + '
    ' + '
    ' + '
    ', compile: compile }; function compile(tElement, tAttrs, transclude) { tElement.attr('aria-valuemin', 0); tElement.attr('aria-valuemax', 100); tElement.attr('role', 'progressbar'); return postLink; } function postLink(scope, element, attr) { $mdTheming(element); var lastMode; var isDisabled = attr.hasOwnProperty('disabled'); var toVendorCSS = $mdUtil.dom.animator.toCss; var bar1 = angular.element(element[0].querySelector('.md-bar1')); var bar2 = angular.element(element[0].querySelector('.md-bar2')); var container = angular.element(element[0].querySelector('.md-container')); element .attr('md-mode', mode()) .toggleClass(DISABLED_CLASS, isDisabled); validateMode(); watchAttributes(); /** * Watch the value, md-buffer-value, and md-mode attributes */ function watchAttributes() { attr.$observe('value', function(value) { var percentValue = clamp(value); element.attr('aria-valuenow', percentValue); if (mode() != MODE_QUERY) animateIndicator(bar2, percentValue); }); attr.$observe('mdBufferValue', function(value) { animateIndicator(bar1, clamp(value)); }); attr.$observe('disabled', function(value) { if (value === true || value === false) { isDisabled = !!value; } else { isDisabled = angular.isDefined(value); } element.toggleClass(DISABLED_CLASS, isDisabled); container.toggleClass(lastMode, !isDisabled); }); attr.$observe('mdMode', function(mode) { if (lastMode) container.removeClass( lastMode ); switch( mode ) { case MODE_QUERY: case MODE_BUFFER: case MODE_DETERMINATE: case MODE_INDETERMINATE: container.addClass( lastMode = "md-mode-" + mode ); break; default: container.addClass( lastMode = "md-mode-" + MODE_INDETERMINATE ); break; } }); } /** * Auto-defaults the mode to either `determinate` or `indeterminate` mode; if not specified */ function validateMode() { if ( angular.isUndefined(attr.mdMode) ) { var hasValue = angular.isDefined(attr.value); var mode = hasValue ? MODE_DETERMINATE : MODE_INDETERMINATE; var info = "Auto-adding the missing md-mode='{0}' to the ProgressLinear element"; //$log.debug( $mdUtil.supplant(info, [mode]) ); element.attr("md-mode", mode); attr.mdMode = mode; } } /** * Is the md-mode a valid option? */ function mode() { var value = (attr.mdMode || "").trim(); if ( value ) { switch(value) { case MODE_DETERMINATE: case MODE_INDETERMINATE: case MODE_BUFFER: case MODE_QUERY: break; default: value = MODE_INDETERMINATE; break; } } return value; } /** * Manually set CSS to animate the Determinate indicator based on the specified * percentage value (0-100). */ function animateIndicator(target, value) { if ( isDisabled || !mode() ) return; var to = $mdUtil.supplant("translateX({0}%) scale({1},1)", [ (value-100)/2, value/100 ]); var styles = toVendorCSS({ transform : to }); angular.element(target).css( styles ); } } /** * Clamps the value to be between 0 and 100. * @param {number} value The value to clamp. * @returns {number} */ function clamp(value) { return Math.max(0, Math.min(value || 0, 100)); } } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.radioButton * @description radioButton module! */ mdRadioGroupDirective.$inject = ["$mdUtil", "$mdConstant", "$mdTheming", "$timeout"]; mdRadioButtonDirective.$inject = ["$mdAria", "$mdUtil", "$mdTheming"]; angular.module('material.components.radioButton', [ 'material.core' ]) .directive('mdRadioGroup', mdRadioGroupDirective) .directive('mdRadioButton', mdRadioButtonDirective); /** * @ngdoc directive * @module material.components.radioButton * @name mdRadioGroup * * @restrict E * * @description * The `` directive identifies a grouping * container for the 1..n grouped radio buttons; specified using nested * `` tags. * * As per the [material design spec](http://www.google.com/design/spec/style/color.html#color-ui-color-application) * the radio button is in the accent color by default. The primary color palette may be used with * the `md-primary` class. * * Note: `` and `` handle tabindex differently * than the native `` controls. Whereas the native controls * force the user to tab through all the radio buttons, `` * is focusable, and by default the ``s are not. * * @param {string} ng-model Assignable angular expression to data-bind to. * @param {boolean=} md-no-ink Use of attribute indicates flag to disable ink ripple effects. * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} ngChange AngularJS expression to be executed when input changes due to user * interaction with the input element. * * @usage * * * * * * {{ d.label }} * * * * * * */ function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming, $timeout) { RadioGroupController.prototype = createRadioGroupControllerProto(); return { restrict: 'E', controller: ['$element', RadioGroupController], require: ['mdRadioGroup', '?ngModel'], link: { pre: linkRadioGroup } }; function linkRadioGroup(scope, element, attr, ctrls) { element.addClass('_md'); // private md component indicator for styling $mdTheming(element); var rgCtrl = ctrls[0]; var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); rgCtrl.init(ngModelCtrl); scope.mouseActive = false; element .attr({ 'role': 'radiogroup', 'tabIndex': element.attr('tabindex') || '0' }) .on('keydown', keydownListener) .on('mousedown', function(event) { scope.mouseActive = true; $timeout(function() { scope.mouseActive = false; }, 100); }) .on('focus', function() { if(scope.mouseActive === false) { rgCtrl.$element.addClass('md-focused'); } }) .on('blur', function() { rgCtrl.$element.removeClass('md-focused'); }); /** * */ function setFocus() { if (!element.hasClass('md-focused')) { element.addClass('md-focused'); } } /** * */ function keydownListener(ev) { var keyCode = ev.which || ev.keyCode; // Only listen to events that we originated ourselves // so that we don't trigger on things like arrow keys in // inputs. if (keyCode != $mdConstant.KEY_CODE.ENTER && ev.currentTarget != ev.target) { return; } switch (keyCode) { case $mdConstant.KEY_CODE.LEFT_ARROW: case $mdConstant.KEY_CODE.UP_ARROW: ev.preventDefault(); rgCtrl.selectPrevious(); setFocus(); break; case $mdConstant.KEY_CODE.RIGHT_ARROW: case $mdConstant.KEY_CODE.DOWN_ARROW: ev.preventDefault(); rgCtrl.selectNext(); setFocus(); break; case $mdConstant.KEY_CODE.ENTER: var form = angular.element($mdUtil.getClosest(element[0], 'form')); if (form.length > 0) { form.triggerHandler('submit'); } break; } } } function RadioGroupController($element) { this._radioButtonRenderFns = []; this.$element = $element; } function createRadioGroupControllerProto() { return { init: function(ngModelCtrl) { this._ngModelCtrl = ngModelCtrl; this._ngModelCtrl.$render = angular.bind(this, this.render); }, add: function(rbRender) { this._radioButtonRenderFns.push(rbRender); }, remove: function(rbRender) { var index = this._radioButtonRenderFns.indexOf(rbRender); if (index !== -1) { this._radioButtonRenderFns.splice(index, 1); } }, render: function() { this._radioButtonRenderFns.forEach(function(rbRender) { rbRender(); }); }, setViewValue: function(value, eventType) { this._ngModelCtrl.$setViewValue(value, eventType); // update the other radio buttons as well this.render(); }, getViewValue: function() { return this._ngModelCtrl.$viewValue; }, selectNext: function() { return changeSelectedButton(this.$element, 1); }, selectPrevious: function() { return changeSelectedButton(this.$element, -1); }, setActiveDescendant: function (radioId) { this.$element.attr('aria-activedescendant', radioId); }, isDisabled: function() { return this.$element[0].hasAttribute('disabled'); } }; } /** * Change the radio group's selected button by a given increment. * If no button is selected, select the first button. */ function changeSelectedButton(parent, increment) { // Coerce all child radio buttons into an array, then wrap then in an iterator var buttons = $mdUtil.iterator(parent[0].querySelectorAll('md-radio-button'), true); if (buttons.count()) { var validate = function (button) { // If disabled, then NOT valid return !angular.element(button).attr("disabled"); }; var selected = parent[0].querySelector('md-radio-button.md-checked'); var target = buttons[increment < 0 ? 'previous' : 'next'](selected, validate) || buttons.first(); // Activate radioButton's click listener (triggerHandler won't create a real click event) angular.element(target).triggerHandler('click'); } } } /** * @ngdoc directive * @module material.components.radioButton * @name mdRadioButton * * @restrict E * * @description * The ``directive is the child directive required to be used within `` elements. * * While similar to the `` directive, * the `` directive provides ink effects, ARIA support, and * supports use within named radio groups. * * @param {string} ngValue AngularJS expression which sets the value to which the expression should * be set when selected. * @param {string} value The value to which the expression should be set when selected. * @param {string=} name Property name of the form under which the control is published. * @param {string=} aria-label Adds label to radio button for accessibility. * Defaults to radio button's text. If no text content is available, a warning will be logged. * * @usage * * * * Label 1 * * * * Green * * * * */ function mdRadioButtonDirective($mdAria, $mdUtil, $mdTheming) { var CHECKED_CSS = 'md-checked'; return { restrict: 'E', require: '^mdRadioGroup', transclude: true, template: '
    ' + '
    ' + '
    ' + '
    ' + '
    ', link: link }; function link(scope, element, attr, rgCtrl) { var lastChecked; $mdTheming(element); configureAria(element, scope); // ngAria overwrites the aria-checked inside a $watch for ngValue. // We should defer the initialization until all the watches have fired. // This can also be fixed by removing the `lastChecked` check, but that'll // cause more DOM manipulation on each digest. if (attr.ngValue) { $mdUtil.nextTick(initialize, false); } else { initialize(); } /** * Initializes the component. */ function initialize() { if (!rgCtrl) { throw 'RadioButton: No RadioGroupController could be found.'; } rgCtrl.add(render); attr.$observe('value', render); element .on('click', listener) .on('$destroy', function() { rgCtrl.remove(render); }); } /** * On click functionality. */ function listener(ev) { if (element[0].hasAttribute('disabled') || rgCtrl.isDisabled()) return; scope.$apply(function() { rgCtrl.setViewValue(attr.value, ev && ev.type); }); } /** * Add or remove the `.md-checked` class from the RadioButton (and conditionally its parent). * Update the `aria-activedescendant` attribute. */ function render() { var checked = rgCtrl.getViewValue() == attr.value; if (checked === lastChecked) return; if (element[0].parentNode.nodeName.toLowerCase() !== 'md-radio-group') { // If the radioButton is inside a div, then add class so highlighting will work element.parent().toggleClass(CHECKED_CSS, checked); } if (checked) { rgCtrl.setActiveDescendant(element.attr('id')); } lastChecked = checked; element .attr('aria-checked', checked) .toggleClass(CHECKED_CSS, checked); } /** * Inject ARIA-specific attributes appropriate for each radio button */ function configureAria(element, scope){ element.attr({ id: attr.id || 'radio_' + $mdUtil.nextUid(), role: 'radio', 'aria-checked': 'false' }); $mdAria.expectWithText(element, 'aria-label'); } } } })(); (function(){ "use strict"; /** * @ngdoc module * @name material.components.select */ /*************************************************** ### TODO - POST RC1 ### - [ ] Abstract placement logic in $mdSelect service to $mdMenu service ***************************************************/ SelectDirective.$inject = ["$mdSelect", "$mdUtil", "$mdConstant", "$mdTheming", "$mdAria", "$parse", "$sce", "$injector"]; SelectMenuDirective.$inject = ["$parse", "$mdUtil", "$mdConstant", "$mdTheming"]; OptionDirective.$inject = ["$mdButtonInkRipple", "$mdUtil", "$mdTheming"]; SelectProvider.$inject = ["$$interimElementProvider"]; var SELECT_EDGE_MARGIN = 8; var selectNextId = 0; var CHECKBOX_SELECTION_INDICATOR = angular.element('
    '); angular.module('material.components.select', [ 'material.core', 'material.components.backdrop' ]) .directive('mdSelect', SelectDirective) .directive('mdSelectMenu', SelectMenuDirective) .directive('mdOption', OptionDirective) .directive('mdOptgroup', OptgroupDirective) .directive('mdSelectHeader', SelectHeaderDirective) .provider('$mdSelect', SelectProvider); /** * @ngdoc directive * @name mdSelect * @restrict E * @module material.components.select * * @description Displays a select box, bound to an ng-model. * * When the select is required and uses a floating label, then the label will automatically contain * an asterisk (`*`). This behavior can be disabled by using the `md-no-asterisk` attribute. * * By default, the select will display with an underline to match other form elements. This can be * disabled by applying the `md-no-underline` CSS class. * * ### Option Params * * When applied, `md-option-empty` will mark the option as "empty" allowing the option to clear the * select and put it back in it's default state. You may supply this attribute on any option you * wish, however, it is automatically applied to an option whose `value` or `ng-value` are not * defined. * * **Automatically Applied** * * - `` * - `` * - `` * - `` * - `` * * **NOT Automatically Applied** * * - `` * - `` * - `` * - `` (this evaluates to the string `"undefined"`) * - <md-option ng-value="{{someValueThatMightBeUndefined}}"> * * **Note:** A value of `undefined` ***is considered a valid value*** (and does not auto-apply this * attribute) since you may wish this to be your "Not Available" or "None" option. * * **Note:** Using the `value` attribute (as opposed to `ng-value`) always evaluates to a string, so * `value="null"` will require the test `ng-if="myValue != 'null'"` rather than `ng-if="!myValue"`. * * @param {expression} ng-model The model! * @param {boolean=} multiple When set to true, allows for more than one option to be selected. The model is an array with the selected choices. * @param {expression=} md-on-close Expression to be evaluated when the select is closed. * @param {expression=} md-on-open Expression to be evaluated when opening the select. * Will hide the select options and show a spinner until the evaluated promise resolves. * @param {expression=} md-selected-text Expression to be evaluated that will return a string * to be displayed as a placeholder in the select input box when it is closed. The value * will be treated as *text* (not html). * @param {expression=} md-selected-html Expression to be evaluated that will return a string * to be displayed as a placeholder in the select input box when it is closed. The value * will be treated as *html*. The value must either be explicitly marked as trustedHtml or * the ngSanitize module must be loaded. * @param {string=} placeholder Placeholder hint text. * @param md-no-asterisk {boolean=} When set to true, an asterisk will not be appended to the * floating label. **Note:** This attribute is only evaluated once; it is not watched. * @param {string=} aria-label Optional label for accessibility. Only necessary if no placeholder or * explicit label is present. * @param {string=} md-container-class Class list to get applied to the `.md-select-menu-container` * element (for custom styling). * * @usage * With a placeholder (label and aria-label are added dynamically) * * * * {{ opt }} * * * * * With an explicit label * * * * * {{ opt }} * * * * * With a select-header * * When a developer needs to put more than just a text label in the * md-select-menu, they should use the md-select-header. * The user can put custom HTML inside of the header and style it to their liking. * One common use case of this would be a sticky search bar. * * When using the md-select-header the labels that would previously be added to the * OptGroupDirective are ignored. * * * * * * Neighborhoods - * * {{ opt }} * * * * * ## Selects and object equality * When using a `md-select` to pick from a list of objects, it is important to realize how javascript handles * equality. Consider the following example: * * angular.controller('MyCtrl', function($scope) { * $scope.users = [ * { id: 1, name: 'Bob' }, * { id: 2, name: 'Alice' }, * { id: 3, name: 'Steve' } * ]; * $scope.selectedUser = { id: 1, name: 'Bob' }; * }); * * *
    * * {{ user.name }} * *
    *
    * * At first one might expect that the select should be populated with "Bob" as the selected user. However, * this is not true. To determine whether something is selected, * `ngModelController` is looking at whether `$scope.selectedUser == (any user in $scope.users);`; * * Javascript's `==` operator does not check for deep equality (ie. that all properties * on the object are the same), but instead whether the objects are *the same object in memory*. * In this case, we have two instances of identical objects, but they exist in memory as unique * entities. Because of this, the select will have no value populated for a selected user. * * To get around this, `ngModelController` provides a `track by` option that allows us to specify a different * expression which will be used for the equality operator. As such, we can update our `html` to * make use of this by specifying the `ng-model-options="{trackBy: '$value.id'}"` on the `md-select` * element. This converts our equality expression to be * `$scope.selectedUser.id == (any id in $scope.users.map(function(u) { return u.id; }));` * which results in Bob being selected as desired. * * Working HTML: * *
    * * {{ user.name }} * *
    *
    */ function SelectDirective($mdSelect, $mdUtil, $mdConstant, $mdTheming, $mdAria, $parse, $sce, $injector) { var keyCodes = $mdConstant.KEY_CODE; var NAVIGATION_KEYS = [keyCodes.SPACE, keyCodes.ENTER, keyCodes.UP_ARROW, keyCodes.DOWN_ARROW]; return { restrict: 'E', require: ['^?mdInputContainer', 'mdSelect', 'ngModel', '?^form'], compile: compile, controller: function() { } // empty placeholder controller to be initialized in link }; function compile(element, attr) { // add the select value that will hold our placeholder or selected option value var valueEl = angular.element(''); valueEl.append(''); valueEl.addClass('md-select-value'); if (!valueEl[0].hasAttribute('id')) { valueEl.attr('id', 'select_value_label_' + $mdUtil.nextUid()); } // There's got to be an md-content inside. If there's not one, let's add it. var mdContentEl = element.find('md-content'); if (!mdContentEl.length) { element.append(angular.element('').append(element.contents())); } mdContentEl.attr('role', 'presentation'); // Add progress spinner for md-options-loading if (attr.mdOnOpen) { // Show progress indicator while loading async // Use ng-hide for `display:none` so the indicator does not interfere with the options list element .find('md-content') .prepend(angular.element( '
    ' + ' ' + '
    ' )); // Hide list [of item options] while loading async element .find('md-option') .attr('ng-show', '$$loadingAsyncDone'); } if (attr.name) { var autofillClone = angular.element(',
    Please do not input invalid characters!
    Please input the description less than 255 characters.
    multiattach readonly {$ source.name $} {$ backup.name $} {$ image.name $} {$ snapshot.name $} {$ volume.name $}
    ././@PaxHeader0000000000000000000000000000027300000000000010217 xustar00159 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volume/os__cinder__volume.js 28 mtime=1790765394.5484958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000672115257164523032615 0ustar00zuulzuul(function() { 'use strict'; /* OS::Cinder::Volume * */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .value('osCinderVolumeSettings', { resource_key: "OS__Cinder__Volume", admin: false, icon: { class: 'fa-hdd-o', name: 'OS::Cinder::Volume', code: '\uf0a0', color: '#0bb238' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: null } ) // Register the resource to globals angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osCinderVolumeSettings','hotgenGlobals', function(osCinderVolumeSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osCinderVolumeSettings.resource_key , osCinderVolumeSettings.icon); hotgenGlobals.update_resource_components( osCinderVolumeSettings.resource_key, osCinderVolumeSettings.modal_component); hotgenGlobals.update_node_labels( osCinderVolumeSettings.resource_key, osCinderVolumeSettings.label); }]); // Define controller function osCinderVolumeController($scope, hotgenGlobals, hotgenNotify, validationRules) { this.$onInit = function(){ if (typeof this.volume.metadata === 'undefined'){ this.volume.metadata = [{}]; } if (typeof this.volume.scheduler_hints === 'undefined'){ this.volume.scheduler_hints = [{}]; } $scope.dependson = this.dependson; }; $scope.boot_sources = [ {'id': 'image', 'name': 'image'}, {'id': 'volume', 'name': 'volume'}, {'id': 'volume_snapshot', 'name': 'volume snapshot'}, {'id': 'backup', 'name': 'backup'}, ]; $scope.options = hotgenGlobals.get_resource_options(); $scope.show_more = false; $scope.validate_name = validationRules['name']; this.delete_metadata = function(index){ this.volume.metadata.splice(index, 1) } this.add_metadata = function(){ this.volume.metadata.push({}) } this.delete_scheduler_hints = function(index){ this.volume.scheduler_hints.splice(index, 1) } this.add_scheduler_hints= function(){ this.volume.scheduler_hints.push({}) } } function osCinderVolumePath (basePath){ return basePath + 'js/resources/os__cinder__volume/os__cinder__volume.html'; } osCinderVolumeController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osCinderVolumePath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osCinderVolume', { templateUrl: osCinderVolumePath, controller: osCinderVolumeController, bindings: { 'volume': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(); ././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00164 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volume/os__cinder__volume.spec.js 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000561115257164523032612 0ustar00zuulzuul(function() { 'use strict'; describe('component os-cinder-volume', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with resource set', function() { $scope.resource = {metadata: [], scheduler_hints:[]}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('metadata should be successfully added', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.add_metadata(); expect($scope.resource.metadata.length).toEqual(2); }); it('metadata should be successfully deleted', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.delete_metadata(); expect($scope.resource.metadata.length).toEqual(0); }); it('scheduler_hints should be successfully added', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.add_scheduler_hints(); expect($scope.resource.scheduler_hints.length).toEqual(2); }); it('scheduler_hints should be successfully deleted', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.delete_scheduler_hints(); expect($scope.resource.scheduler_hints.length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000027300000000000010217 xustar00160 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volume/os__cinder__volume.svg 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000536315257164523032616 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000026000000000000010213 xustar00148 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/ 28 mtime=1790765446.3732605 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000032400000000000010214 xustar00185 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-blue.svg 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001312115257164523032605 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032400000000000010214 xustar00185 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-gray.svg 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001312115257164523032605 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032500000000000010215 xustar00186 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-green.svg 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001311115257164523032604 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032300000000000010213 xustar00184 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-red.svg 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001312115257164523032605 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032000000000000010210 xustar00181 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.html 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000424315257164523032612 0ustar00zuulzuul {$ iuuid.name $}
    You must supply an instance uuid.
    {$ vol.name $}
    You must supply an volume id.
    Please input a valid device path.
    ././@PaxHeader0000000000000000000000000000031600000000000010215 xustar00179 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.js 27 mtime=1790765394.549496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001370515257164523032615 0ustar00zuulzuul(function() { 'use strict'; /* OS::Cinder::VolumeAttachment * */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osCinderVolumeAttachmentSettings', { resource_key: "OS__Cinder__VolumeAttachment", admin: false, icon: { class: 'fa-plug', name: 'OS::Cinder::VolumeAttachment', code: '\uf1e6', color: '#0bb238' }, label: 'mountpoint', modal_component: '', edge_settings: { 'OS__Cinder__Volume': { 'type': 'property', 'property': 'volume_id', 'limit': 1, 'occupied': false, //* whether can be connected to any other resource */ 'lonely': false, //* whether can be connected to one more other resource */ 'handler': osCinderVolumeAttachmentController.handle_edge_volume_id, 'modal': null }, 'OS__Nova__Server': { 'type': 'property', 'property': 'instance_uuid', 'limit': 1, 'occupied': false, 'handler': osCinderVolumeAttachmentController.handle_edge_instance_uuid, 'modal': null }, }, necessary_properties: { 'instance_uuid': ['OS__Nova__Server',], 'volume_id': ['OS__Cinder__Volume',] } } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osCinderVolumeAttachmentSettings', 'hotgenGlobals', function(osCinderVolumeAttachmentSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osCinderVolumeAttachmentSettings.resource_key, osCinderVolumeAttachmentSettings.icon); hotgenGlobals.update_node_labels( osCinderVolumeAttachmentSettings.resource_key, osCinderVolumeAttachmentSettings.label); hotgenGlobals.update_resource_components( osCinderVolumeAttachmentSettings.resource_key, osCinderVolumeAttachmentSettings.modal_component); hotgenGlobals.update_edge_directions( osCinderVolumeAttachmentSettings.resource_key, osCinderVolumeAttachmentSettings.edge_settings); }]); // Define controller function osCinderVolumeAttachmentController($scope, hotgenGlobals, validationRules){ this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } this.disable = {'instance_uuid': false, 'volume_id': false} $scope.update = { volumes: $scope.get_volume_id_options(), instances: $scope.get_instance_uuid_options() } if ( $scope.connected_options.instance_uuid && $scope.connected_options.instance_uuid.length > 0){ this.volumeattachment['instance_uuid'] = $scope.connected_options.instance_uuid[0].value this.disable.instance_uuid = true } if ( $scope.connected_options.volume_id && $scope.connected_options.volume_id.length > 0){ this.volumeattachment['volume_id'] = $scope.connected_options.volume_id[0].value this.disable.volume_id = true } $scope.dependson = this.dependson; } $scope.options = hotgenGlobals.get_resource_options(); $scope.validatePath = validationRules['path']; $scope.get_volume_id_options = function(){ if ('volume_id' in $scope.connected_options){ var resource_volumes = []; for (var idx in $scope.connected_options.volume_id){ var item = $scope.connected_options.volume_id[idx]; resource_volumes.push({ id: item.value, name: item.value }) } return $scope.options.volumes.concat(resource_volumes); } return $scope.options.volumes; } $scope.get_instance_uuid_options = function(){ if ('instance_uuid' in $scope.connected_options){ var resource_instances = []; for (var idx in $scope.connected_options.instance_uuid){ var item = $scope.connected_options.instance_uuid[idx]; resource_instances.push({ id: item.value, name: item.value }) } return $scope.options.instances.concat(resource_instances); } return $scope.options.instances; } } osCinderVolumeAttachmentController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules',]; osCinderVolumeAttachmentPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osCinderVolumeAttachmentPath (basePath){ return basePath + 'js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osCinderVolumeAttachment', { templateUrl: osCinderVolumeAttachmentPath, controller: osCinderVolumeAttachmentController, bindings:{ 'volumeattachment': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(); ././@PaxHeader0000000000000000000000000000032400000000000010214 xustar00184 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.spec.js 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000602615257164523032613 0ustar00zuulzuul(function() { 'use strict'; describe('os-cinder-volume-attachment', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ instances: [{id: 'instance1-id', name: 'instance1'}], volumes: [{id: 'volume1-id', name: 'volume1-id'}], }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function() { $scope.resource = {instance_uuid: 'instance-uuid', volume_id: 'volume-id'} $scope.connectedoptions = {instance_uuid: [{value: 'instance-uuid'}], volume_id: [{value: 'volume-id'}]}; element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); expect($isolateScope.$ctrl.volumeattachment.instance_uuid).toEqual('instance-uuid'); expect($isolateScope.$ctrl.volumeattachment.volume_id).toEqual('volume-id'); }); }); })(); ././@PaxHeader0000000000000000000000000000032000000000000010210 xustar00180 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.svg 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001311315257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000025400000000000010216 xustar00144 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/ 28 mtime=1790765446.3762603 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000031500000000000010214 xustar00177 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-blue.svg 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000212615257164523032610 0ustar00zuulzuul Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031500000000000010214 xustar00177 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-gray.svg 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000212615257164523032610 0ustar00zuulzuul Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031600000000000010215 xustar00178 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-green.svg 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000211315257164523032604 0ustar00zuulzuul Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031400000000000010213 xustar00176 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-red.svg 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000212615257164523032610 0ustar00zuulzuul Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031100000000000010210 xustar00173 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.html 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000667715257164523032627 0ustar00zuulzuul
    You must supply a name.
    That doesn't look like a valid name.
    Too long name.
    You must supply a zone.
    That doesn't look like a valid id of zone.
    {$ type.name $}
    At least one record is required.
    {$ 'Show More Properties' | translate $}
    Too long description.
    ././@PaxHeader0000000000000000000000000000030700000000000010215 xustar00171 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.js 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001015315257164523032607 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Designate::RecordSet angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osDesignateRecordsetSettings', { resource_key: "OS__Designate__RecordSet", admin: false, icon: { class: 'fa-key ', name: 'OS::Designate::RecordSet', code: '\uf084', color: '#483dff' }, label: 'name', modal_component: '', edge_settings: { 'OS__Designate__Zone': { 'type': 'property', 'property': 'zone', 'limit': 1, }, }, necessary_properties: { 'name': null, 'zone': ['OS__Designate__Zone'], 'type': null, 'records': null, }, } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osDesignateRecordsetSettings', 'hotgenGlobals', function( osDesignateRecordsetSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osDesignateRecordsetSettings.resource_key, osDesignateRecordsetSettings.icon); hotgenGlobals.update_resource_components( osDesignateRecordsetSettings.resource_key, osDesignateRecordsetSettings.modal_component); hotgenGlobals.update_edge_directions( osDesignateRecordsetSettings.resource_key, osDesignateRecordsetSettings.edge_settings); hotgenGlobals.update_node_labels( osDesignateRecordsetSettings.resource_key, osDesignateRecordsetSettings.label); }]); function osDesignateRecordsetController($scope, hotgenGlobals, validationRules){ $scope.options = hotgenGlobals.get_resource_options(); $scope.options.types = [ {'id': 'A', 'name': 'A'}, {'id': 'AAAA', 'name': 'AAAA'}, {'id': 'MX', 'name': 'MX'}, {'id': 'CNAME', 'name': 'CNAME'}, {'id': 'TXT', 'name': 'TXT'}, {'id': 'SRV', 'name': 'SRV'}, {'id': 'NS', 'name': 'NS'}, {'id': 'PTR', 'name': 'PTR'}, {'id': 'SPF', 'name': 'SPF'}, {'id': 'SSHFP', 'name': 'SSHFP'}, ]; $scope.admin = $scope.options.auth.admin; this.$onInit = function(){ if (typeof this.recordset.records === 'undefined'){ this.recordset.records = []; } if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } this.disable = {'zone': false} if ( $scope.connected_options.zone && $scope.connected_options.zone.length > 0){ this.recordset.zone = $scope.connected_options.zone[0].value this.disable.zone = true } $scope.dependson = this.dependson; } $scope.validate_name = validationRules['name']; } osDesignateRecordsetController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osDesignateRecordsetPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osDesignateRecordsetPath(basePath){ return basePath + 'js/resources/os__designate__recordset/os__designate__recordset.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osDesignateRecordset', { templateUrl: osDesignateRecordsetPath, controller: osDesignateRecordsetController, bindings:{ 'recordset': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000031400000000000010213 xustar00176 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.spec.js 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000336715257164523032620 0ustar00zuulzuul(function() { 'use strict'; describe('component os-designate-recordset', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title Properties with resource properties set', function() { $scope.resource = { masters: [], attributes: []}; element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); expect(element.find('span').html()).toContain("Properties"); }); }); })(); ././@PaxHeader0000000000000000000000000000031000000000000010207 xustar00172 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.svg 28 mtime=1790765394.5504959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000211515257164523032606 0ustar00zuulzuul Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000024700000000000010220 xustar00139 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/ 28 mtime=1790765446.3782604 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000030300000000000010211 xustar00167 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-blue.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000176315257164523032616 0ustar00zuulzuul Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000030300000000000010211 xustar00167 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-gray.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000175715257164523032621 0ustar00zuulzuul Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000030400000000000010212 xustar00168 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-green.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000175015257164523032612 0ustar00zuulzuul Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00166 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-red.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000176315257164523032616 0ustar00zuulzuul Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.html 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000627715257164523032623 0ustar00zuulzuul
    You must supply a name.
    That doesn't look like a valid name.
    Too long name.
    You must supply an email.
    Too long description.
    {$ 'Show More Properties' | translate $}
    {$ type.name $}
    ././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.js 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000634415257164523032616 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Designate::Zone angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osDesignateZoneSettings', { resource_key: "OS__Designate__Zone", admin: false, icon: { class: 'fa-key ', name: 'OS::Designate::Zone', code: '\uf084', color: '#483dff' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: { 'name': null, 'email': null }, } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osDesignateZoneSettings', 'hotgenGlobals', function( osDesignateZoneSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osDesignateZoneSettings.resource_key, osDesignateZoneSettings.icon); hotgenGlobals.update_resource_components( osDesignateZoneSettings.resource_key, osDesignateZoneSettings.modal_component); hotgenGlobals.update_edge_directions( osDesignateZoneSettings.resource_key, osDesignateZoneSettings.edge_settings); hotgenGlobals.update_node_labels( osDesignateZoneSettings.resource_key, osDesignateZoneSettings.label); }]); function osDesignateZoneController($scope, hotgenGlobals, hotgenNotify, validationRules){ $scope.options = hotgenGlobals.get_resource_options(); $scope.options.types = [ {'id': 'PRIMARY', 'name': 'PRIMARY'}, {'id': 'SECONDARY', 'name': 'SECONDARY'} ]; $scope.admin = $scope.options.auth.admin; this.$onInit = function(){ if (typeof this.zone.masters === 'undefined'){ this.zone.masters = []; } $scope.dependson = this.dependson; } $scope.validate_zone = validationRules['zone']; $scope.validate_master = function (input_string){ var re = /^[A-Za-z0-9_.-]+$/; var match = re.exec(input_string); if (match){ return input_string; } else{ hotgenNotify.show_error('Invalid master value.'); return null; } } } osDesignateZoneController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osDesignateZonePath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osDesignateZonePath(basePath){ return basePath + 'js/resources/os__designate__zone/os__designate__zone.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osDesignateZone', { templateUrl: osDesignateZonePath, controller: osDesignateZoneController, bindings:{ 'zone': '=', 'dependson': '=', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00166 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.spec.js 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000371615257164523032616 0ustar00zuulzuul(function() { 'use strict'; describe('component os-designate-zone', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''+ ''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title Properties with resource properties set', function() { $scope.resource = { masters: [], attributes: []}; element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); expect(element.find('span').html()).toContain("Properties"); }); it('test validate master function', function() { expect($isolateScope.validate_master('192.168.1.1')).toEqual('192.168.1.1'); expect($isolateScope.validate_master('localhost')).toEqual('localhost'); expect($isolateScope.validate_master('xxx@gmail.com')).toEqual(null); }); }); })(); ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000175215257164523032614 0ustar00zuulzuul Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000025600000000000010220 xustar00146 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/ 28 mtime=1790765446.3812602 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000032100000000000010211 xustar00181 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-blue.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000470515257164523032615 0ustar00zuulzuul Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000032100000000000010211 xustar00181 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-gray.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000460015257164523032607 0ustar00zuulzuulLayer 1Layer 2 ././@PaxHeader0000000000000000000000000000032200000000000010212 xustar00182 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-green.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000467715257164523032625 0ustar00zuulzuul Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000032000000000000010210 xustar00180 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-red.svg 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000470515257164523032615 0ustar00zuulzuul Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000031500000000000010214 xustar00177 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.html 28 mtime=1790765394.5514958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002055215257164523032613 0ustar00zuulzuul
    You did not enter a valid integer.
    You did not enter a non-negative number.
    You must supply a maximum size.
    You must supply an integer.
    You did not enter a valid integer.
    You did not enter a non-negative number.
    You must supply a minimum size.
    You must supply an integer.
    existing items uploading a template file
    You did not specify Resource Type.
    {$ resource_type $}
    choose a file
    You did not enter a valid integer.
    You did not enter a non-negative number.
    You must supply an integer.
    You did not enter a valid integer.
    You did not enter a non-negative number.
    You must supply an integer.
    You did not enter a valid integer.
    You did not enter a non-negative number.
    You must supply an integer.
    You did not enter a valid number.
    You did not enter a non-negative number.
    You did not enter a valid integer.
    You did not enter a positive number.
    You must supply an integer.
    ././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.js 27 mtime=1790765394.552496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001302715257164523032612 0ustar00zuulzuul(function() { 'use strict'; /* OS::Heat::AutoScalingGroup * */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .value('osHeatAutoScalingGroupSettings', { resource_key: "OS__Heat__AutoScalingGroup", admin: false, icon: { class: 'fa-hdd-o', name: 'OS::Heat::AutoScalingGroup', code: '\uf0a0', color: '#0bb238' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: ['max_size', 'min_size', 'resource'] } ) // Register the resource to globals angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osHeatAutoScalingGroupSettings','hotgenGlobals', function(osHeatAutoScalingGroupSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osHeatAutoScalingGroupSettings.resource_key , osHeatAutoScalingGroupSettings.icon); hotgenGlobals.update_resource_components( osHeatAutoScalingGroupSettings.resource_key, osHeatAutoScalingGroupSettings.modal_component); hotgenGlobals.update_node_labels( osHeatAutoScalingGroupSettings.resource_key, osHeatAutoScalingGroupSettings.label); }]); // Define controller function osHeatAutoScalingGroupController($scope, hotgenGlobals, hotgenNotify, validationRules) { this.$onInit = function(){ $scope.dependson = this.dependson; if (typeof this.autoscalinggroup.resource === 'undefined'){ this.autoscalinggroup.resource = {'type': '', 'properties': [{}]}; } if (typeof this.autoscalinggroup.rolling_updates === 'undefined'){ this.autoscalinggroup.rolling_updates = {}; } if (this.autoscalinggroup.resource.type){ $scope.filecontent = hotgenGlobals.get_reference_file(this.autoscalinggroup.resource.type); } }; $scope.validate_integer = validationRules['integer']; $scope.controller = this; $scope.resource_types = []; var resource_types = hotgenGlobals.get_resource_types(); resource_types.forEach(function(element){ $scope.resource_types.push(element.replace(/_/g, ':')); }); $scope.options = hotgenGlobals.get_resource_options(); $scope.update_upload = function(){ if ($scope.controller.autoscalinggroup && $scope.controller.autoscalinggroup.resource && $scope.controller.autoscalinggroup.resource.type.indexOf('.yaml') != -1){ return 'true'; } return 'false'; } $scope.is_upload = $scope.update_upload(); $scope.$watch('is_upload', function(newValue, oldValue){ if (oldValue === newValue){ return; } if (newValue === true || newValue === 'true'){ $scope.controller.autoscalinggroup.resource.properties = [{}]; if (!($scope.filecontent && $scope.filecontent.length >= 0)){ $scope.controller.autoscalinggroup.resource.type = ''; } } else{ // ; } }); $scope.file_upload = function(element){ var file = element.files[0]; if (!file){ return; } var textType = /\.yaml$/; if (file.name.match(textType)) { var reader = new FileReader(); $scope.controller.autoscalinggroup.resource.type = file.name; reader.onload = function(e) { hotgenNotify.show_success('Read file content.'); hotgenGlobals.set_reference_file(file.name, reader.result) $scope.filecontent = reader.result; } reader.readAsText(file); } else { hotgenNotify.show_error('File type is not supported.'); } } $scope.clickUpload = function(){ setTimeout(function () { angular.element('#resourcetype-file').trigger('click'); }, 0); }; this.delete_property = function(index){ this.autoscalinggroup.resource.properties.splice(index, 1); } this.add_property = function(){ this.autoscalinggroup.resource.properties.push({}); } } function osHeatAutoScalingGroupPath (basePath){ return basePath + 'js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.html'; } osHeatAutoScalingGroupController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osHeatAutoScalingGroupPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osHeatAutoscalinggroup', { templateUrl: osHeatAutoScalingGroupPath, controller: osHeatAutoScalingGroupController, bindings: { 'autoscalinggroup': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(); ././@PaxHeader0000000000000000000000000000031700000000000010216 xustar00180 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.spec.js 27 mtime=1790765394.552496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001104215257164523032605 0ustar00zuulzuul(function() { 'use strict'; describe('component os-heat-auto-scaling-group', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenNotify; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenNotify = $injector.get('hotgenNotify'); spyOn(hotgenNotify, 'show_success'); spyOn(hotgenNotify, 'show_error'); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); expect($isolateScope.is_upload).toEqual('false'); }); it('find tab title with resource set', function() { $scope.resource = {resource: {type: 'filepath.yaml'}}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(' Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000025300000000000010215 xustar00143 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/ 28 mtime=1790765446.3842602 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-blue.svg 27 mtime=1790765394.552496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002415415257164523032615 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-gray.svg 27 mtime=1790765394.552496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002415415257164523032615 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031300000000000010212 xustar00176 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-green.svg 27 mtime=1790765394.552496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002413215257164523032611 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031100000000000010210 xustar00174 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-red.svg 27 mtime=1790765394.552496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002415415257164523032615 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030600000000000010214 xustar00171 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.html 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001350515257164523032613 0ustar00zuulzuul
    You did not enter a valid number.
    You did not enter a positive number.
    existing items uploading a template file
    You did not select Resource Reference Type.
    {$ resource_type $}
    choose a file
    {$ 'Show More Properties' | translate $}
    ././@PaxHeader0000000000000000000000000000030400000000000010212 xustar00169 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.js 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001261415257164523032613 0ustar00zuulzuul(function() { 'use strict'; /* OS::Cinder::Volume * */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .value('osHeatResourceGroupSettings', { resource_key: "OS__Heat__ResourceGroup", admin: false, icon: { class: 'fa-server', name: 'OS::Heat::ResourceGroup', code: '\uf233', color: '#0bb238' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: null } ); // Register the resource to globals angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osHeatResourceGroupSettings','hotgenGlobals', function(osHeatResourceGroupSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osHeatResourceGroupSettings.resource_key , osHeatResourceGroupSettings.icon); hotgenGlobals.update_resource_components( osHeatResourceGroupSettings.resource_key, osHeatResourceGroupSettings.modal_component); hotgenGlobals.update_node_labels( osHeatResourceGroupSettings.resource_key, osHeatResourceGroupSettings.label); }]); function osHeatResourceGroupController ($scope, hotgenGlobals, hotgenNotify, validationRules) { this.$onInit = function(){ if (typeof this.resourcegroup.resource_def === 'undefined'){ this.resourcegroup.resource_def = {'type': '', 'properties': [{}], 'metadata': []}; } $scope.dependson = this.dependson; if (this.resourcegroup.resource_def.type){ $scope.filecontent = hotgenGlobals.get_reference_file(this.resourcegroup.resource_def.type); } }; $scope.controller = this; $scope.resource_types = []; var resource_types = hotgenGlobals.get_resource_types(); resource_types.forEach(function(element){ $scope.resource_types.push(element.replace(/_/g, ':')); }); $scope.update_upload = function(){ if ($scope.controller.resourcegroup && $scope.controller.resourcegroup.resource_def && $scope.controller.resourcegroup.resource_def.type.indexOf('.yaml') != -1){ return 'true'; } return 'false'; } $scope.is_upload = $scope.update_upload(); $scope.show_more = false; $scope.$watch('is_upload', function(newValue, oldValue){ if (oldValue === newValue){ return; } if (newValue === true || newValue === 'true'){ $scope.controller.resourcegroup.resource_def.properties = [{}]; if (!($scope.filecontent && $scope.filecontent.length >= 0)){ $scope.controller.resourcegroup.resource_def.type = ''; } } else{ // ; } }); $scope.file_upload = function(element){ var file = element.files[0]; if (!file){ return; } var textType = /\.yaml$/; if (file.name.match(textType)) { var reader = new FileReader(); $scope.controller.resourcegroup.resource_def.type = file.name; reader.onload = function(e) { hotgenNotify.show_success('Read file content.'); hotgenGlobals.set_reference_file(file.name, reader.result) $scope.filecontent = reader.result; } reader.readAsText(file); } else { hotgenNotify.show_error('File type is not supported.'); } } $scope.clickUpload = function(){ setTimeout(function () { angular.element('#resourceref-file').trigger('click'); }, 0); }; this.delete_property = function(index){ this.resourcegroup.resource_def.properties.splice(index, 1); } this.add_property = function(){ this.resourcegroup.resource_def.properties.push({}); } this.delete_metadata = function(index){ this.resourcegroup.resource_def.metadata.splice(index, 1); } this.add_metadata = function(){ this.resourcegroup.resource_def.metadata.push({}); } } function osHeatResourceGroupPath (basePath){ return basePath + 'js/resources/os__heat__resourcegroup/os__heat__resourcegroup.html'; } osHeatResourceGroupController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osHeatResourceGroupPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osHeatResourceGroup', { templateUrl: osHeatResourceGroupPath, controller: osHeatResourceGroupController, bindings: { 'resourcegroup': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(); ././@PaxHeader0000000000000000000000000000031100000000000010210 xustar00174 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.spec.js 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001204015257164523032604 0ustar00zuulzuul(function() { 'use strict'; describe('component os-heat-resource-group', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenNotify; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenNotify = $injector.get('hotgenNotify'); spyOn(hotgenNotify, 'show_success'); spyOn(hotgenNotify, 'show_error'); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); expect($isolateScope.is_upload).toEqual('false'); }); it('find tab title with resource set', function() { $scope.resource = {resource_def: {type: 'filepath.yaml'}}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); expect($isolateScope.is_upload ).toEqual('true'); }); it('$scope.is_upload should be successfully watched', function() { $isolateScope.is_upload = 'true'; $isolateScope.$digest(); $isolateScope.is_upload = 'false'; $isolateScope.$digest(); $isolateScope.filecontent = 'some thing here'; $isolateScope.is_upload = 'true'; $isolateScope.$digest(); expect($isolateScope.controller.resourcegroup.resource_def.type ).toEqual(''); }); it('file should be successfully uploaded', function() { var blob = new Blob([''], {type: '', }); blob['name'] = 'filename.yaml' var upload_element = {files: [blob], } spyOn(window, 'FileReader').and.returnValue({ readAsText: function(file) { this.onload({}); }, result: 'file contents.' }); $isolateScope.file_upload(upload_element); expect($isolateScope.filecontent).toEqual('file contents.'); expect(hotgenNotify.show_success).toHaveBeenCalled(); }); it('file should be not uploaded return undefined', function() { var upload_element = {files: []} var returnValue = $isolateScope.file_upload(upload_element); expect(returnValue).toEqual(undefined); }); it('file should be not uploaded show error', function() { var upload_element = {files: [{name: 'file.txt'}]} var returnValue = $isolateScope.file_upload(upload_element); expect(hotgenNotify.show_error).toHaveBeenCalled(); }); it('metadata should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_metadata(); expect($ctrl.resourcegroup.resource_def.metadata.length).toEqual(1); }); it('metadata should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_metadata(); $ctrl.delete_metadata(); expect($ctrl.resourcegroup.resource_def.metadata.length).toEqual(0); }); it('property should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_property(); expect($ctrl.resourcegroup.resource_def.properties.length).toEqual(2); }); it('property should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_property(); expect($ctrl.resourcegroup.resource_def.properties.length).toEqual(0); }); it('click upload', function() { spyOn(window, 'setTimeout').and.callFake(function(){}); $isolateScope.clickUpload(); expect(setTimeout).toHaveBeenCalled(); }); }); })(); ././@PaxHeader0000000000000000000000000000030500000000000010213 xustar00170 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.svg 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002411215257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000025100000000000010213 xustar00143 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/ 26 mtime=1790765446.38626 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-blue.svg 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000511515257164523032611 0ustar00zuulzuul Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-gray.svg 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000512015257164523032605 0ustar00zuulzuul Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000031300000000000010212 xustar00176 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-green.svg 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000510315257164523032606 0ustar00zuulzuul Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000031100000000000010210 xustar00174 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-red.svg 27 mtime=1790765394.553496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000511515257164523032611 0ustar00zuulzuul Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000030700000000000010215 xustar00171 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.html 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000627315257164523032617 0ustar00zuulzuul {$ at.name $}
    You did not enter a valid number.
    You must supply a scaling adjustment size.
    You must supply a valid ID.
    You must supply a auto scaling group ID.
    You did not enter a valid number.
    You did not enter a positive number.
    You did not enter a valid integer.
    You did not enter a positive number.
    You must supply an integer.
    ././@PaxHeader0000000000000000000000000000030500000000000010213 xustar00169 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.js 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001250115257164523032606 0ustar00zuulzuul(function() { 'use strict'; /* OS::Heat::ScalingPolicy * */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .value('osHeatScalingPolicySettings', { resource_key: "OS__Heat__ScalingPolicy", admin: false, icon: { class: 'fa-clone', name: 'OS::Heat::ScalingPolicy', code: '\uf24d', color: '#0bb238' }, label: 'name', modal_component: '', edge_settings: { 'OS__Heat__AutoScalingGroup': { 'type': 'property', 'property': 'auto_scaling_group_id', 'limit': 1, 'occupied': false, //* whether can be connected to any other resource */ 'lonely': false, //* whether can be connected to one more other resource */ 'modal': null } }, necessary_properties: ['adjustment_type', 'scaling_adjustment', 'auto_scaling_group_id'] } ) // Register the resource to globals angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osHeatScalingPolicySettings','hotgenGlobals', function(osHeatScalingPolicySettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osHeatScalingPolicySettings.resource_key , osHeatScalingPolicySettings.icon); hotgenGlobals.update_resource_components( osHeatScalingPolicySettings.resource_key, osHeatScalingPolicySettings.modal_component); hotgenGlobals.update_node_labels( osHeatScalingPolicySettings.resource_key, osHeatScalingPolicySettings.label); hotgenGlobals.update_edge_directions( osHeatScalingPolicySettings.resource_key, osHeatScalingPolicySettings.edge_settings); }]); // Define controller function osHeatScalingPolicyController($scope, hotgenGlobals, hotgenNotify, validationRules) { this.$onInit = function(){ $scope.dependson = this.dependson; $scope.min_adj_step_disabled = false; if (this.scalingpolicy.adjustment_type !=='percent_change_in_capacity'){ this.scalingpolicy.min_adjustment_step = ''; $scope.min_adj_step_disabled = true; } $scope.auto_scaling_group_id_disabled = false; $scope.auto_scaling_group_id_pattern = $scope.validate_uuid4; if (this.connectedoptions.auto_scaling_group_id){ for (var idx in this.connectedoptions.auto_scaling_group_id){ this.scalingpolicy.auto_scaling_group_id = this.connectedoptions.auto_scaling_group_id[idx].value; break; } $scope.auto_scaling_group_id_disabled = true; $scope.auto_scaling_group_id_pattern = ""; } else{ if (this.scalingpolicy.auto_scaling_group_id && this.scalingpolicy.auto_scaling_group_id.indexOf('get_resource') >= 0){ // no edge with AutoScalingGroup but still holding get_resource dependency, // consider edge is removed and empty auto_scaling_group_id this.scalingpolicy.auto_scaling_group_id = ""; } $scope.auto_scaling_group_id_disabled = false; } }; $scope.validate_integer = validationRules['integer']; $scope.validate_uuid4= validationRules['uuid4']; $scope.controller = this; $scope.adjustment_types = [ {'value': 'change_in_capacity', 'name': 'Change in Capacity'}, {'value': 'exact_capacity', 'name': 'Exact Capacity'}, {'value': 'percent_change_in_capacity', 'name': 'Percent Change in Capacity'}, ]; $scope.$watch('controller.scalingpolicy.adjustment_type', function(newValue, oldValue){ if (oldValue === newValue){ return; } if (newValue === 'percent_change_in_capacity'){ $scope.min_adj_step_disabled = false; } else{ $scope.controller.scalingpolicy.min_adjustment_step = ''; $scope.min_adj_step_disabled = true; } }); } function osHeatScalingPolicyPath (basePath){ return basePath + 'js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.html'; } osHeatScalingPolicyController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osHeatScalingPolicyPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osHeatScalingpolicy', { templateUrl: osHeatScalingPolicyPath, controller: osHeatScalingPolicyController, bindings: { 'scalingpolicy': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })();././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00174 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.spec.js 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000773315257164523032621 0ustar00zuulzuul(function() { 'use strict'; describe('component os-heat-scalingpolicy', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.resourceForm = {}; $scope.connectedoptions = []; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''+ ''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function() { $scope.resource = {auto_scaling_group_id: 'auto-scaling-group-uuid'} $scope.connectedoptions = {auto_scaling_group_id: [{value: 'auto-scaling-group-uuid'}], }; element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); expect($isolateScope.$ctrl.scalingpolicy.auto_scaling_group_id).toEqual('auto-scaling-group-uuid'); }); it('find tab title with auto_scaling_group_id set', function() { $scope.resource = {auto_scaling_group_id: '{ get_resource: AutoScalingGroup_1 }'} element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); expect($isolateScope.$ctrl.scalingpolicy.auto_scaling_group_id).toEqual(''); }); it('find tab title with adjustment type set', function() { $scope.resource = {adjustment_type: 'percent_change_in_capacity'} element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); $scope.resource = {adjustment_type: 'change_in_capacity'} element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); }); })(); ././@PaxHeader0000000000000000000000000000030600000000000010214 xustar00170 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.svg 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000511015257164523032604 0ustar00zuulzuul Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000025100000000000010213 xustar00143 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/ 26 mtime=1790765446.38926 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000031300000000000010212 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-blue.svg 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000421215257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031300000000000010212 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-gray.svg 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000421215257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031400000000000010213 xustar00176 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-green.svg 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000420615257164523032611 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00174 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-red.svg 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000421215257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030700000000000010215 xustar00171 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.html 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001215015257164523032606 0ustar00zuulzuul {$ fnet.name $}
    You must supply an floating network.
    Please input a valid IP address.
    Please input a valid IP address.
    {$ 'Show More Properties' | translate $}
    {$ fsubnet.name $} {$ port.name $}
    Please input a valid DNS Domain.
    Please input a valid DNS Name.
    ././@PaxHeader0000000000000000000000000000030500000000000010213 xustar00169 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.js 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001574115257164523032617 0ustar00zuulzuul(function(angular) { 'use strict'; /* OS::Neutron::FloatingIP */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronFloatingipSettings', { resource_key: "OS__Neutron__FloatingIP", admin: false, icon: { class: 'fa-neuter', name: 'OS::Neutron::FloatingIP', code: '\uf22c', color: '#40a5f2' }, label: 'floating_ip_address', modal_component: '', edge_settings: { 'OS__Neutron__Net': { 'type': 'property', 'property': 'floating_network', 'limit': 1, }, 'OS__Neutron__Port': { 'type': 'property', 'property': 'port_id', 'limit': 1, }, 'OS__Neutron__Subnet': { 'type': 'property', 'property': 'floating_subnet', 'limit': 1, }, }, necessary_properties: { 'floating_network': ['OS__Neutron__Net'] }, } ) angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronFloatingipSettings', 'hotgenGlobals', function(osNeutronFloatingipSettings, hotgenGlobals){ hotgenGlobals.update_node_labels( osNeutronFloatingipSettings.resource_key, osNeutronFloatingipSettings.label); hotgenGlobals.update_resource_icons( osNeutronFloatingipSettings.resource_key, osNeutronFloatingipSettings.icon); hotgenGlobals.update_resource_components( osNeutronFloatingipSettings.resource_key, osNeutronFloatingipSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronFloatingipSettings.resource_key, osNeutronFloatingipSettings.edge_settings); }]); function osNeutronFloatingipController($scope, hotgenGlobals, validationRules){ this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } $scope.dependson = this.dependson; this.disable = { 'floating_network': false, 'floating_subnet': false, 'port_id': false } $scope.update = { floating_networks: $scope.get_floating_network_options(), floating_subnets: $scope.get_floating_subnet_options(), ports: $scope.get_port_options(), } if (typeof this.floatingip.value_specs == 'undefined'){ this.floatingip.value_specs = [{}] } if ( $scope.connected_options.floating_network && $scope.connected_options.floating_network.length > 0){ this.floatingip['floating_network'] = $scope.connected_options.floating_network[0].value this.disable.floating_network = true } if ( $scope.connected_options.floating_subnet && $scope.connected_options.floating_subnet.length > 0){ this.floatingip['floating_subnet'] = $scope.connected_options.floating_subnet[0].value this.disable.floating_subnet = true } if ( $scope.connected_options.port_id && $scope.connected_options.port_id.length > 0){ this.floatingip['port_id'] = $scope.connected_options.port_id[0].value this.disable.port_id = true } } $scope.show_more = false; $scope.validate_ip_address = validationRules['ip_address']; $scope.options = hotgenGlobals.get_resource_options(); this.add_value_specs = function(){ this.floatingip.value_specs.push({}) } this.delete_value_specs = function(index){ this.floatingip.value_specs.splice(index, 1) } $scope.get_floating_network_options = function(){ if ('floating_network' in $scope.connected_options){ var resource_floating_network = []; for (var idx in $scope.connected_options.floating_network){ var item = $scope.connected_options.floating_network[idx]; resource_floating_network.push({ id: item.value, name: item.value }) } return $scope.options.floating_networks.concat(resource_floating_network); } return $scope.options.floating_networks; } $scope.get_floating_subnet_options = function(){ if ('floating_subnet' in $scope.connected_options){ var resource_floating_subnet = []; for (var idx in $scope.connected_options.floating_subnet){ var item = $scope.connected_options.floating_subnet[idx]; resource_floating_subnet.push({ id: item.value, name: item.value }) } return $scope.options.floating_subnets.concat(resource_floating_subnet); } return $scope.options.floating_subnets; } $scope.get_port_options = function(){ if ('port_id' in $scope.connected_options){ var resource_port = []; for (var idx in $scope.connected_options.port_id){ var item = $scope.connected_options.port_id[idx]; resource_port.push({ id: item.value, name: item.value }) } return $scope.options.ports.concat(resource_port); } return $scope.options.ports; } } osNeutronFloatingipController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNeutronFloatingipPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronFloatingipPath(basePath){ return basePath + 'js/resources/os__neutron__floatingip/os__neutron__floatingip.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osNeutronFloatingip', { templateUrl: osNeutronFloatingipPath, controller: osNeutronFloatingipController, bindings:{ 'floatingip': '=', 'dependson': '=', 'formReference': '<', 'connectedoptions': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00174 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.spec.js 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000707315257164523032616 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-floatingip', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ floating_networks: [{id: 'floating-network1-id', name: 'floating-network1-id'}], floating_subnets: [{id: 'floating-subnet1-id', name: 'floating-subnet1-id'}], ports: [{id: 'port1-id', name: 'port1-id'}], }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function() { $scope.resource = {floating_network: 'floating-network-id', floating_subnet: 'floating-subnet-id', port_id: 'port_id'} $scope.connectedoptions = {floating_network: [{value: 'floating-network-id'}], floating_subnet: [{value: 'floating-subnet-id'}], port_id: [{value: 'port-id'}], }; element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('value_spec should be successfully added', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.add_value_specs(); expect($scope.resource.value_specs.length).toEqual(2); }); it('value_spec should be successfully deleted', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.delete_value_specs(); expect($scope.resource.value_specs.length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000030600000000000010214 xustar00170 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.svg 28 mtime=1790765394.5544958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000420615257164523032611 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000026400000000000010217 xustar00154 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/ 26 mtime=1790765446.39226 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000033600000000000010217 xustar00194 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingassociation.spec.js 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000573115257164523032615 0ustar00zuulzuul(function() { 'use strict'; describe('os-neutron-floatingip-association', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ floatingips: [{id: 'floatingip1-id', name: 'floatingip1-id'}], ports: [{id: 'port1-id', name: 'port1-id'}], }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''+ ''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function() { $scope.resource = {floatingip_id: 'floatingip-id', port_id: 'port_id'} $scope.connectedoptions = {floatingip_id: [{value: 'floatingip-id'}], port_id: [{value: 'port-id'}], }; element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); }); })(); ././@PaxHeader0000000000000000000000000000034100000000000010213 xustar00197 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-blue.svg 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000704315257164523032613 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000034100000000000010213 xustar00197 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-gray.svg 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000704315257164523032613 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000034200000000000010214 xustar00198 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-green.svg 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000703315257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000034000000000000010212 xustar00196 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-red.svg 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000704315257164523032613 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000033500000000000010216 xustar00193 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.html 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000410015257164523032602 0ustar00zuulzuul {$ fip.id $}
    You must supply a floating IP.
    {$ port.name $}
    You must supply a port.
    Please input a valid IP address.
    ././@PaxHeader0000000000000000000000000000033300000000000010214 xustar00191 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.js 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001302515257164523032610 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::FloatingIPAssociation angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronFloatingipAssociationSettings', { resource_key: "OS__Neutron__FloatingIPAssociation", admin: false, icon: { class: 'fa-paperclip', name: 'OS::Neutron::FloatingIPAssociation', code: '\uf0c6', color: '#40a5f2' }, label: 'fixed_ip_address', modal_component: '', edge_settings: { 'OS__Neutron__FloatingIP': { 'type': 'property', 'property': 'floatingip_id', 'limit': 1, }, 'OS__Neutron__Port': { 'type': 'property', 'property': 'port_id', 'limit': 1, }, }, necessary_properties: { 'floatingip_id': ['OS__Neutron__FloatingIP'], 'port_id': ['OS__Neutron__Port'], } } ) angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronFloatingipAssociationSettings', 'hotgenGlobals', function(osNeutronFloatingipAssociationSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronFloatingipAssociationSettings.resource_key, osNeutronFloatingipAssociationSettings.icon); hotgenGlobals.update_resource_components( osNeutronFloatingipAssociationSettings.resource_key, osNeutronFloatingipAssociationSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronFloatingipAssociationSettings.resource_key, osNeutronFloatingipAssociationSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronFloatingipAssociationSettings.resource_key, osNeutronFloatingipAssociationSettings.label); }]); function osNeutronFloatingipAssociationController($scope, hotgenGlobals, validationRules){ this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = []; } else{ $scope.connected_options = this.connectedoptions; } this.disable = {'floatingip_id': false, 'port_id': false} $scope.dependson = this.dependson; $scope.update = { floatingips: $scope.get_floatingip_options(), ports: $scope.get_port_options(), } if ( $scope.connected_options.floatingip_id && $scope.connected_options.floatingip_id.length > 0){ this.floatingipassociation['floatingip_id'] = $scope.connected_options.floatingip_id[0].value; this.disable.floatingip_id = true; } if ( $scope.connected_options.port_id && $scope.connected_options.port_id.length > 0){ this.floatingipassociation['port_id'] = $scope.connected_options.port_id[0].value; this.disable.port_id = true; } } $scope.options = hotgenGlobals.get_resource_options(); $scope.validate_ip_address = validationRules['ip_address']; $scope.get_floatingip_options = function(){ if ('floatingip_id' in $scope.connected_options){ var resource_fip = []; for (var idx in $scope.connected_options.floatingip_id){ var item = $scope.connected_options.floatingip_id[idx]; resource_fip.push({ id: item.value, }) } return $scope.options.floatingips.concat(resource_fip); } return $scope.options.floatingips; } $scope.get_port_options = function(){ if ('port_id' in $scope.connected_options){ var resource_port = []; for (var idx in $scope.connected_options.port_id){ var item = $scope.connected_options.port_id[idx]; resource_port.push({ id: item.value, name: item.value }) } return $scope.options.ports.concat(resource_port); } return $scope.options.ports; } } osNeutronFloatingipAssociationController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNeutronFloatingipAssociationPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronFloatingipAssociationPath(basePath){ return basePath + 'js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronFloatingipAssociation', { templateUrl: osNeutronFloatingipAssociationPath, controller: osNeutronFloatingipAssociationController, bindings:{ 'floatingipassociation': '=', 'dependson': '=', 'formReference': '<', 'connectedoptions': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000033400000000000010215 xustar00192 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.svg 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000704215257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000024200000000000010213 xustar00136 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/ 26 mtime=1790765446.39426 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-blue.svg 28 mtime=1790765394.5554957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000300615257164523032606 0ustar00zuulzuul Layer 1././@PaxHeader0000000000000000000000000000027400000000000010220 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-gray.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000300615257164523032606 0ustar00zuulzuul Layer 1././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-green.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000300215257164523032602 0ustar00zuulzuul Layer 1././@PaxHeader0000000000000000000000000000027300000000000010217 xustar00160 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-red.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000300615257164523032606 0ustar00zuulzuul Layer 1././@PaxHeader0000000000000000000000000000027000000000000010214 xustar00157 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.html 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001257115257164523032615 0ustar00zuulzuul
    That doesn't look like a valid network name.
    Too long network name.
    Please input a valid DNS Domain.
    Admin State UP Port Security Enabled Shared
    {$ 'Show More Properties' | translate $}
    {$ qos.name $} ({$ qos.id $})
    That doesn't look like a valid tenant id.
    ././@PaxHeader0000000000000000000000000000026600000000000010221 xustar00155 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.js 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000657315257164523032622 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::Net angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronNetSettings', { resource_key: "OS__Neutron__Net", admin: false, icon: { class: 'fa-cloud', name: 'OS::Neutron::Net', code: '\uf0c2', color: '#40a5f2' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: null } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronNetSettings', 'hotgenGlobals', function(osNeutronNetSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronNetSettings.resource_key, osNeutronNetSettings.icon); hotgenGlobals.update_resource_components( osNeutronNetSettings.resource_key, osNeutronNetSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronNetSettings.resource_key, osNeutronNetSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronNetSettings.resource_key, osNeutronNetSettings.label); }]); function osNeutronNetController($scope, hotgenGlobals, validationRules){ this.$onInit = function(){ this.admin = $scope.options.auth.admin; if (typeof this.network.tags == 'undefined'){ this.network.tags = [] } if (typeof this.network.dhcp_agent_ids === 'undefined'){ this.network.dhcp_agent_ids = []; } if (typeof this.network.admin_state_up === 'undefined'){ this.network.admin_state_up = true; } if (typeof this.network.value_specs == 'undefined'){ this.network.value_specs = [{}] } $scope.dependson = this.dependson; }; this.add_value_specs = function(){ this.network.value_specs.push({}) } this.delete_value_specs = function(index){ this.network.value_specs.splice(index, 1) } $scope.options = hotgenGlobals.get_resource_options(); $scope.qos_policies = $scope.options.qos_policies; $scope.show_more = false; $scope.validate_name = validationRules['name']; $scope.validate_domain = validationRules['domain']; $scope.validate_uuid_nohyphe = validationRules['uuid_nohyphen']; } osNeutronNetController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNeutronNetPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronNetPath(basePath){ return basePath + 'js/resources/os__neutron__net/os__neutron__net.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronNet', { templateUrl: osNeutronNetPath, controller: osNeutronNetController, bindings:{ 'network': '=', 'dependson': '=', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000027300000000000010217 xustar00160 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.spec.js 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000417115257164523032612 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-net', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''+ ''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title Properties with resource properties set', function() { $scope.resource = { tags: [], dhcp_agent_ids: [], admin_state_up: true, value_specs: []}; element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); expect(element.find('span').html()).toContain("Properties"); }); it('value_spec should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_value_specs(); expect($scope.resource.value_specs.length).toEqual(2); }); it('value_spec should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_value_specs(); expect($scope.resource.value_specs.length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000026700000000000010222 xustar00156 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000300215257164523032602 0ustar00zuulzuul Layer 1././@PaxHeader0000000000000000000000000000024500000000000010216 xustar00137 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/ 28 mtime=1790765446.3962598 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-blue.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000430215257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-gray.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000430215257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00164 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-green.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000427615257164523032620 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-red.svg 27 mtime=1790765394.556496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000430215257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027300000000000010217 xustar00159 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.html 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002411115257164523032606 0ustar00zuulzuul
    That doesn't look like a valid port name.
    Too long port name.
    {$ nw.name $}
    You must supply a network.
    {$ sg.name $}
    Admin State UP Port Security Enabled
    {$ 'Show More Properties' | translate $}
    That doesn't look like a valid mac address.
    That doesn't look like a valid ip address.

    That doesn't look like a valid ip address.
    {$ subnet.name $}
    {$ item.display $} Warning: "{$ $ctrl.searchText $}" saved with no pre-defined device owner found. {$ qos.name $}
    That doesn't look like a valid mac address.
    ././@PaxHeader0000000000000000000000000000027100000000000010215 xustar00157 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.js 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002313415257164523032612 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::Port angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronPortSettings', { resource_key: "OS__Neutron__Port", admin: false, icon: { class: 'fa-genderless', name: 'OS::Neutron::Port', code: '\uf22d', color: '#40a5f2', }, label: 'name', modal_component: '', edge_settings: { 'OS__Neutron__Net': { 'type': 'property', 'property': 'network', 'limit': 1, }, 'OS__Neutron__Subnet': { 'type': 'complex', 'property': 'fixed_ips.subnet', 'limit': 99, }, 'OS__Neutron__SecurityGroup': { 'type': 'list', 'property': 'security_groups', 'limit': 99, }, }, necessary_properties: { 'network': ['OS__Neutron__Net'], } } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronPortSettings', 'hotgenGlobals', function(osNeutronPortSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronPortSettings.resource_key, osNeutronPortSettings.icon); hotgenGlobals.update_resource_components( osNeutronPortSettings.resource_key, osNeutronPortSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronPortSettings.resource_key, osNeutronPortSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronPortSettings.resource_key, osNeutronPortSettings.label); }]); function osNeutronPortController($scope, hotgenGlobals, validationRules){ this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } this.disable = { 'network': false, 'subnets': [], 'security_groups': [], } if (typeof this.port.admin_state_up === 'undefined'){ this.port.admin_state_up = true; } if (typeof this.port.binding === 'undefined'){ this.port.binding = {'vnic_type': ''}; } if (typeof this.port.allowed_address_pairs === 'undefined'){ this.port.allowed_address_pairs = [{}]; } if (typeof this.port.fixed_ips === 'undefined'){ this.port.fixed_ips = [{}]; } if (typeof this.port.security_groups === 'undefined'){ this.port.security_groups = []; } if (typeof this.port.value_specs == 'undefined'){ this.port.value_specs = [{}] } if (this.port.device_owner){ this.searchText = this.port.device_owner; } if ( $scope.connected_options.network && $scope.connected_options.network.length > 0){ this.port['network'] = $scope.connected_options.network[0].value; this.disable.network = true; } if ( $scope.connected_options.security_groups && $scope.connected_options.security_groups.length > 0){ for (var idx in $scope.connected_options.security_groups){ this.port['security_groups'].push($scope.connected_options.security_groups[idx].value); this.disable.security_groups.push($scope.connected_options.security_groups[idx].value); } } if ( $scope.connected_options['fixed_ips.subnet'] && $scope.connected_options['fixed_ips.subnet'].length > 0){ for (var idx in $scope.connected_options['fixed_ips.subnet']){ this.port.fixed_ips.splice(0,0, {subnet: $scope.connected_options['fixed_ips.subnet'][idx].value}); this.disable.subnets.push($scope.connected_options['fixed_ips.subnet'][idx].value); } } $scope.update = { networks: $scope.get_networks_options(), security_groups: $scope.get_security_groups_options(), subnets: $scope.get_subnets_options(), } $scope.dependson = this.dependson; }; $scope.options = hotgenGlobals.get_resource_options(); $scope.show_more = false; $scope.validate_name = validationRules['name']; $scope.validate_mac_address = validationRules['mac_address']; $scope.validate_ip_address = validationRules['ip_address']; this.device_owners = load_device_owners(); this.querySearch = querySearch; this.show_not_found = true; this.selectedItemChange = selectedItemChange; this.searchTextChange = searchTextChange; $scope.get_networks_options = function(){ if ('network' in $scope.connected_options){ var resource_nw = []; for (var idx in $scope.connected_options.network){ var item = $scope.connected_options.network[idx]; resource_nw.push({ id: item.value, name: item.value }) } return $scope.options.networks.concat(resource_nw); } return $scope.options.networks; } $scope.get_security_groups_options = function(){ if ('security_groups' in $scope.connected_options){ var resource_secgroups = []; for (var idx in $scope.connected_options.security_groups){ var item = $scope.connected_options.security_groups[idx]; resource_secgroups.push({ id: item.value, name: item.value }) } return $scope.options.security_groups.concat(resource_secgroups); } return $scope.options.security_groups; } $scope.get_subnets_options = function(){ if ('fixed_ips.subnet' in $scope.connected_options){ var resource_subnets = []; for (var idx in $scope.connected_options['fixed_ips.subnet']){ var item = $scope.connected_options['fixed_ips.subnet'][idx]; resource_subnets.push({ id: item.value, name: item.value }) } return $scope.options.subnets.concat(resource_subnets); } return $scope.options.subnets; } $scope.qos_policies = $scope.options.qos_policies; this.add_value_specs = function(){ this.port.value_specs.push({}) } this.delete_value_specs = function(index){ this.port.value_specs.splice(index, 1) } this.add_allowed_address_pair = function(){ this.port.allowed_address_pairs.push({}) } this.add_fixed_ip = function(){ this.port.fixed_ips.push({}) } this.delete_allowed_address_pair = function(index){ this.port.allowed_address_pairs.splice(index, 1) } this.delete_fixed_ip = function(index){ this.port.fixed_ips.splice(index, 1) } function searchTextChange(text) { this.port.device_owner = text; } function selectedItemChange(item) { this.port.device_owner = item.display; } function load_device_owners(){ var all_device_owners = 'network:floatingip, network:router_interface, network:dhcp'; return all_device_owners.split(/, +/g).map(function(dev_owner){ return { value: dev_owner.replace(/:/g, '_'), display: dev_owner } }); } function querySearch (query) { return query ? this.device_owners.filter( createFilterFor(query) ) : this.device_owners; } /** * Create filter function for a query string */ function createFilterFor(query) { var lowercaseQuery = angular.lowercase(query).replace(/:/g, '_'); return function filterFn(dev_owner) { return (dev_owner.value.indexOf(lowercaseQuery) === 0); }; } } osNeutronPortController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNeutronPortPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronPortPath(basePath){ return basePath + 'js/resources/os__neutron__port/os__neutron__port.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronPort', { templateUrl: osNeutronPortPath, controller: osNeutronPortController, bindings:{ 'port': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.spec.js 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001246015257164523032612 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-port', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ networks: [{id: 'network1-id', name: 'network1-id'}], security_groups: [{id: 'secgroup1-id', name: 'secgroup1-id'}], subnets: [{id: 'subnet1-id', name: 'subnet-id'}] }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function() { $scope.resource = {device_owner: 'someone', network: 'network-id', security_groups: ['secgroup-id'], 'fixed_ips.subnet': ['subnet-id'],} $scope.connectedoptions = {network: [{value: 'network-id'}], security_groups: [{value: 'secgroup-id'}], 'fixed_ips.subnet': [{value: 'subnet-id'}], }; element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('value_spec should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_value_specs(); expect($scope.resource.value_specs.length).toEqual(2); }); it('value_spec should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_value_specs(); expect($scope.resource.value_specs.length).toEqual(0); }); it('allowed address pair should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_allowed_address_pair(); expect($scope.resource.allowed_address_pairs.length).toEqual(2); }); it('allowed address pair should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_allowed_address_pair(); expect($scope.resource.allowed_address_pairs.length).toEqual(0); }); it('fixed_ip should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_fixed_ip(); expect($scope.resource.fixed_ips.length).toEqual(2); }); it('fixed_ip should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_fixed_ip(); expect($scope.resource.fixed_ips.length).toEqual(0); }); it('searchTextChange set device_owner', function(){ var $ctrl = $isolateScope.$ctrl; $ctrl.searchTextChange('device_owner_01'); expect($scope.resource.device_owner).toEqual('device_owner_01'); }); it('selectedItemChange set device_owner', function(){ var $ctrl = $isolateScope.$ctrl; $ctrl.selectedItemChange({display: 'device_owner_01'}); expect($scope.resource.device_owner).toEqual('device_owner_01'); }); it('querySearch set device_owner', function(){ var $ctrl = $isolateScope.$ctrl; var retValue = $ctrl.querySearch(''); expect(retValue.length).toEqual(3); retValue = $ctrl.querySearch('network:dhcp'); expect(retValue).toEqual([ { value: 'network_dhcp', display: 'network:dhcp' }]); }); }); })(); ././@PaxHeader0000000000000000000000000000027200000000000010216 xustar00158 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.svg 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000427615257164523032620 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000024700000000000010220 xustar00139 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/ 28 mtime=1790765446.3992598 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000030300000000000010211 xustar00167 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-blue.svg 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000251215257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030300000000000010211 xustar00167 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-gray.svg 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000251215257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030400000000000010212 xustar00168 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-green.svg 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000250615257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00166 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-red.svg 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000251215257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.html 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001440515257164523032613 0ustar00zuulzuul
    That doesn't look like a valid network name.
    Too long network name.
    {$ nw.name $}
    That doesn't look like a valid ip address.
    {$ subnet.name $}
    Admin State UP Enable SNAT
    {$ 'Show More Properties' | translate $}
    Distributed HA
    ././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.js 28 mtime=1790765394.5574958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001555115257164523032616 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::Router angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronRouterSettings', { resource_key: "OS__Neutron__Router", // admin: true, icon: { class: 'fa-life-bouy', name: 'OS::Neutron::Router', code: '\uf1cd', color: '#40a5f2' }, label: 'name', modal_component: '', edge_settings: { 'OS__Neutron__Subnet': { 'type': 'property', 'property': 'ext_fixed_ips.subnet', 'limit': 99, }, 'OS__Neutron__Net': { 'type': 'property', 'property': 'external_gateway_info.network', 'limit': 1, }, }, necessary_properties: null } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronRouterSettings', 'hotgenGlobals', function(osNeutronRouterSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronRouterSettings.resource_key, osNeutronRouterSettings.icon); hotgenGlobals.update_node_admin( osNeutronRouterSettings.resource_key, osNeutronRouterSettings.admin); hotgenGlobals.update_resource_components( osNeutronRouterSettings.resource_key, osNeutronRouterSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronRouterSettings.resource_key, osNeutronRouterSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronRouterSettings.resource_key, osNeutronRouterSettings.label); }]); function osNeutronRouterController($scope, hotgenGlobals, validationRules) { this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } if (typeof this.router.external_gateway_info === 'undefined'){ this.router.external_gateway_info = {"external_fixed_ips": [{}]}; } if (typeof this.router.l3_agent_ids === 'undefined'){ this.router.l3_agent_ids = []; } if (typeof this.router.tags == 'undefined'){ this.router.tags = [] } if (typeof this.router.admin_state_up === 'undefined'){ this.router.admin_state_up = true; } if (typeof this.router.value_specs == 'undefined'){ this.router.value_specs = [{}] } this.disable = { 'subnets': [], 'network': false, } if ( $scope.connected_options['ext_fixed_ips.subnet'] && $scope.connected_options['ext_fixed_ips.subnet'].length > 0){ for (var idx in $scope.connected_options['ext_fixed_ips.subnet']){ this.router.external_gateway_info.external_fixed_ips.splice(0,0, {subnet: $scope.connected_options['ext_fixed_ips.subnet'][idx].value}); this.disable.subnets.push($scope.connected_options['ext_fixed_ips.subnet'][idx].value); } } if ( $scope.connected_options['external_gateway_info.network'] && $scope.connected_options['external_gateway_info.network'].length > 0){ this.router.external_gateway_info['network'] = $scope.connected_options['external_gateway_info.network'][0].value; this.disable.network = true } $scope.update = {subnets: $scope.get_subnets_options(), networks: $scope.get_networks_options()} $scope.dependson = this.dependson; }; $scope.options = hotgenGlobals.get_resource_options(); $scope.show_more = false; $scope.validate_name = validationRules['name']; $scope.validate_ip_address = validationRules['ip_address']; this.add_external_fixed_ip = function(){ this.router.external_gateway_info.external_fixed_ips.push({}) } this.delete_external_fixed_ip = function(index){ this.router.external_gateway_info.external_fixed_ips.splice(index, 1) } this.add_value_specs = function(){ this.router.value_specs.push({}) } this.delete_value_specs = function(index){ this.router.value_specs.splice(index, 1) } $scope.get_networks_options = function(){ if ('external_gateway_info.network' in $scope.connected_options){ var resource_networks = []; for (var idx in $scope.connected_options['external_gateway_info.network']){ var item = $scope.connected_options['external_gateway_info.network'][idx]; resource_networks.push({ id: item.value, name: item.value }) } return $scope.options.floating_networks.concat(resource_networks); } return $scope.options.floating_networks; } $scope.get_subnets_options = function(){ if ('ext_fixed_ips.subnet' in $scope.connected_options){ var resource_subnets = []; for (var idx in $scope.connected_options['ext_fixed_ips.subnet']){ var item = $scope.connected_options['ext_fixed_ips.subnet'][idx]; resource_subnets.push({ id: item.value, name: item.value }) } return $scope.options.subnets.concat(resource_subnets); } return $scope.options.subnets; } } osNeutronRouterController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNeutronRouterPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronRouterPath(basePath){ return basePath + 'js/resources/os__neutron__router/os__neutron__router.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronRouter', { templateUrl: osNeutronRouterPath, controller: osNeutronRouterController, bindings:{ 'router': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00166 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.spec.js 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000764315257164523032621 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-router', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ networks: [{id: 'network1-id', name: 'network1-id'}], floating_networks: [{id: 'network2-id', name: 'network2'}], subnets: [{id: 'subnet1-id', name: 'subnet-id'}] }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function(){ $scope.resource = {'external_gateway_info.network': 'network-id', 'ext_fixed_ips.subnet': ['subnet-id'],} $scope.connectedoptions = {'external_gateway_info.network': [{value: 'network-id'}], 'ext_fixed_ips.subnet': [{value: 'subnet-id'}], }; element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('value_spec should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_value_specs(); expect($scope.resource.value_specs.length).toEqual(2); }); it('value_spec should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_value_specs(); expect($scope.resource.value_specs.length).toEqual(0); }); it('external_fixed_ip should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_external_fixed_ip(); expect($scope.resource.external_gateway_info.external_fixed_ips.length).toEqual(2); }); it('external_fixed_ip should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_external_fixed_ip(); expect($scope.resource.external_gateway_info.external_fixed_ips.length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.svg 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000250615257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000026000000000000010213 xustar00148 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/ 28 mtime=1790765446.4012597 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000032500000000000010215 xustar00185 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-blue.svg 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000567515257164523032624 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032500000000000010215 xustar00185 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-gray.svg 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000567515257164523032624 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032600000000000010216 xustar00186 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-green.svg 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000566315257164523032621 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032400000000000010214 xustar00184 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-red.svg 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000567515257164523032624 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032100000000000010211 xustar00181 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.html 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000360515257164523032613 0ustar00zuulzuul

    {$ $ctrl.message $}

    {$ router.name $}
    You must supply a router.
    {$ port.name $} {$ subnet.name $}
    ././@PaxHeader0000000000000000000000000000031700000000000010216 xustar00179 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.js 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001411515257164523032611 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::RouterInterface angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronRouterInterfaceSettings', { resource_key: "OS__Neutron__RouterInterface", admin: false, icon: { class: 'fa-sun-o', name: 'OS::Neutron::RouterInterface', code: '\uf185', color: '#40a5f2' }, modal_component: '', edge_settings: { 'OS__Neutron__Port': { 'type': 'property', 'property': 'port', 'limit': 1, }, 'OS__Neutron__Router': { 'type': 'property', 'property': 'router', 'limit': 1, }, 'OS__Neutron__Subnet': { 'type': 'property', 'property': 'subnet', 'limit': 1, }, }, necessary_properties: { 'router': ['OS__Neutron__Router'], } } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronRouterInterfaceSettings', 'hotgenGlobals',function( osNeutronRouterInterfaceSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronRouterInterfaceSettings.resource_key, osNeutronRouterInterfaceSettings.icon); hotgenGlobals.update_resource_components( osNeutronRouterInterfaceSettings.resource_key, osNeutronRouterInterfaceSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronRouterInterfaceSettings.resource_key, osNeutronRouterInterfaceSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronRouterInterfaceSettings.resource_key, osNeutronRouterInterfaceSettings.label); }]); function osNeutronRouterInterfaceController($scope, hotgenGlobals) { this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } this.disable = {'port': false, 'router': false, 'subnet': false} $scope.update = { subnets: $scope.get_subnets_options(), routers: $scope.get_routers_options(), ports: $scope.get_ports_options(), } if ( $scope.connected_options.port && $scope.connected_options.port.length > 0){ this.routerinterface['port'] = $scope.connected_options.port[0].value this.disable.port = true } if ( $scope.connected_options.router && $scope.connected_options.router.length > 0){ this.routerinterface['router'] = $scope.connected_options.router[0].value this.disable.router = true } if ( $scope.connected_options.subnet && $scope.connected_options.subnet.length > 0){ this.routerinterface['subnet'] = $scope.connected_options.subnet[0].value this.disable.subnet = true } $scope.dependson = this.dependson; } $scope.options = hotgenGlobals.get_resource_options(); $scope.get_subnets_options = function(){ if ('subnet' in $scope.connected_options){ var resource_subnets = []; for (var idx in $scope.connected_options.subnet){ var item = $scope.connected_options.subnet[idx]; resource_subnets.push({ id: item.value, name: item.value }) } return $scope.options.subnets.concat(resource_subnets); } return $scope.options.subnets; } $scope.get_ports_options = function(){ if ('port' in $scope.connected_options){ var resource_ports = []; for (var idx in $scope.connected_options.port){ var item = $scope.connected_options.port[idx]; resource_ports.push({ id: item.value, name: item.value }) } return $scope.options.ports.concat(resource_ports); } return $scope.options.ports; } $scope.get_routers_options = function(){ if ('router' in $scope.connected_options){ var resource_routers = []; for (var idx in $scope.connected_options.router){ var item = $scope.connected_options.router[idx]; resource_routers.push({ id: item.value, name: item.value }) } return $scope.options.routers.concat(resource_routers); } return $scope.options.routers; } } osNeutronRouterInterfaceController.$inject = ['$scope', 'hotgenGlobals', ]; osNeutronRouterInterfacePath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronRouterInterfacePath(basePath){ return basePath + 'js/resources/os__neutron__routerinterface/os__neutron__routerinterface.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronRouterInterface', { templateUrl: osNeutronRouterInterfacePath, controller: osNeutronRouterInterfaceController, bindings:{ 'routerinterface': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000032400000000000010214 xustar00184 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.spec.js 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000613315257164523032612 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-routerinterface', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ routers: [{id: 'router1-id', name: 'router1-id'}], subnets: [{id: 'subnet1-id', name: 'subnet1-id'}], ports: [{id: 'port1-id', name: 'port1-id'}], }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''+ ''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with connectedoptions set', function(){ $scope.resource = {port: 'port-id', subnet: 'subnet-id', router: 'router-id'} $scope.connectedoptions = {port: [{value: 'port-id'}], subnet: [{value: 'subnet-id'}], router: [{value: 'router-id'}], }; element = $compile(angular.element(''+ ''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); }); })(); ././@PaxHeader0000000000000000000000000000032000000000000010210 xustar00180 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.svg 28 mtime=1790765394.5584958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000567415257164523032623 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000025600000000000010220 xustar00146 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/ 28 mtime=1790765446.4042597 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000032000000000000010210 xustar00181 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-blue.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000334015257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032000000000000010210 xustar00181 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-gray.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000334015257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000032100000000000010211 xustar00182 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-green.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000333415257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031700000000000010216 xustar00180 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-red.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000334015257164523032607 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000031400000000000010213 xustar00177 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.html 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001314215257164523032610 0ustar00zuulzuul
    You must supply an volume name.
    That doesn't look like a valid volume name.
    Too long volume name.
    Please do not input invalid characters!
    Please input the description less than 255 characters.
    ingress egress IPv4 IPv6 TCP UDP ICMP remote_ip_prefix remote_group_id
    It doesn't looks like a valid IP prefix!
    ././@PaxHeader0000000000000000000000000000031200000000000010211 xustar00175 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.js 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000567115257164523032620 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::SecurityGroup angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronSecurityGroupSettings', { resource_key: "OS__Neutron__SecurityGroup", admin: false, icon: { class: 'fa-shield ', name: 'OS::Neutron::SecurityGroup', code: '\uf132', color: '#40a5f2' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: null } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronSecurityGroupSettings', 'hotgenGlobals', function(osNeutronSecurityGroupSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronSecurityGroupSettings.resource_key, osNeutronSecurityGroupSettings.icon); hotgenGlobals.update_resource_components( osNeutronSecurityGroupSettings.resource_key, osNeutronSecurityGroupSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronSecurityGroupSettings.resource_key, osNeutronSecurityGroupSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronSecurityGroupSettings.resource_key, osNeutronSecurityGroupSettings.label); }]); function osNeutronSecurityGroupController($scope, validationRules) { this.$onInit = function(){ if (typeof this.securitygroup.rules === 'undefined'){ this.securitygroup.rules = [{}]; } $scope.dependson = this.dependson; } this.add_rule = function(){ this.securitygroup.rules.push({}) } this.delete_rule = function(index){ this.securitygroup.rules.splice(index, 1) } $scope.validate_name = validationRules['name']; $scope.validate_cidr = validationRules['cidr']; } osNeutronSecurityGroupController.$inject = ['$scope', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules',]; osNeutronSecurityGroupPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronSecurityGroupPath(basePath){ return basePath + 'js/resources/os__neutron__securitygroup/os__neutron__securitygroup.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronSecurityGroup', { templateUrl: osNeutronSecurityGroupPath, controller: osNeutronSecurityGroupController, bindings:{ 'securitygroup': '=', 'dependson': '=', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000031700000000000010216 xustar00180 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.spec.js 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000421015257164523032604 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-security-group', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title Properties with rules undefined', function() { $scope.resource = {rules: []} element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); it('rules should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_rule(); expect($scope.resource.rules.length).toEqual(2); }); it('rules should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_rule(); expect($scope.resource.rules.length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000031300000000000010212 xustar00176 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000333415257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000024700000000000010220 xustar00139 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/ 28 mtime=1790765446.4062595 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00167 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-blue.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000612415257164523032612 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00167 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-gray.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000612315257164523032611 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030300000000000010211 xustar00168 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-green.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000611715257164523032614 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030100000000000010207 xustar00166 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-red.svg 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000612315257164523032611 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.html 27 mtime=1790765394.559496 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000002202615257164523032611 0ustar00zuulzuul
    That doesn't look like a valid network name.
    Too long network name.
    {$ nw.name $}
    You must supply a network.
    4 6
    Please input a valid cidr range.
    Enable DHCP
    {$ 'Show More Properties' | translate $}
    Please input a valid address.
    That doesn't look like a valid subnet pool id.
    That doesn't look like a valid ip address.
    That doesn't look like a valid ip address.
    That doesn't look like a valid ip address.
    That doesn't look like a valid ip address.
    That doesn't look like a valid tenant id.
    ././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.js 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000001263715257164523032620 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Neutron::Subnet angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNeutronSubnetSettings', { resource_key: "OS__Neutron__Subnet", admin: false, icon: { class: 'fa-cloud-upload ', name: 'OS::Neutron::Subnet', code: '\uf0ee', color: '#40a5f2' }, label: 'name', modal_component: '', edge_settings: { 'OS__Neutron__Net': { 'type': 'property', 'property': 'network', 'limit': 1, }, }, necessary_properties: { 'network': ['OS__Neutron__Net'] } } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNeutronSubnetSettings', 'hotgenGlobals', function(osNeutronSubnetSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNeutronSubnetSettings.resource_key, osNeutronSubnetSettings.icon); hotgenGlobals.update_resource_components( osNeutronSubnetSettings.resource_key, osNeutronSubnetSettings.modal_component); hotgenGlobals.update_edge_directions( osNeutronSubnetSettings.resource_key, osNeutronSubnetSettings.edge_settings); hotgenGlobals.update_node_labels( osNeutronSubnetSettings.resource_key, osNeutronSubnetSettings.label); }]); function osNeutronSubnetController($scope, hotgenGlobals, hotgenNotify, validationRules) { this.$onInit = function(){ this.admin = $scope.options.auth.admin; if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } this.disable = {'network': false} $scope.networks = $scope.get_networks_options(); if (typeof this.subnet.allocation_pools === 'undefined'){ this.subnet.allocation_pools = [{}]; } if (typeof this.subnet.host_routes === 'undefined'){ this.subnet.host_routes = [{}]; } if (typeof this.subnet.dns_nameservers === 'undefined'){ this.subnet.dns_nameservers = []; } if ( $scope.connected_options.network && $scope.connected_options.network.length > 0){ this.subnet['network'] = $scope.connected_options.network[0].value this.disable.network = true } $scope.dependson = this.dependson; } this.add_allocation_pool = function(){ this.subnet.allocation_pools.push({}) } this.delete_allocation_pool = function(index){ this.subnet.allocation_pools.splice(index, 1) } this.add_hostroute = function(){ this.subnet.host_routes.push({}) } this.delete_hostroute = function(index){ this.subnet.host_routes.splice(index, 1) } $scope.validate_dns = function (input_string){ var re = /^[A-Za-z0-9_.-]+$/; var match = re.exec(input_string); if (match){ return input_string; } else{ hotgenNotify.show_error('Invalid name server address.'); return null; } } $scope.options = hotgenGlobals.get_resource_options(); $scope.validate_name = validationRules['name']; $scope.validate_ip_address = validationRules['ip_address']; $scope.validate_cidr = validationRules['cidr']; $scope.validate_uuid4 = validationRules['uuid4']; $scope.validate_uuid_nohyphen = validationRules['uuid_nohyphen']; $scope.get_networks_options = function(){ if ('network' in $scope.connected_options){ var resource_nws = []; for (var idx in $scope.connected_options.network){ var item = $scope.connected_options.network[idx]; resource_nws.push({ id: item.value, name: item.value }) } return $scope.options.networks.concat(resource_nws); } return $scope.options.networks; } } osNeutronSubnetController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNeutronSubnetPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNeutronSubnetPath(basePath){ return basePath + 'js/resources/os__neutron__subnet/os__neutron__subnet.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNeutronSubnet', { templateUrl: osNeutronSubnetPath, controller: osNeutronSubnetController, bindings:{ 'subnet': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000030200000000000010210 xustar00166 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.spec.js 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000721415257164523032613 0ustar00zuulzuul(function() { 'use strict'; describe('component os-neutron-subnet', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenGlobals.update_resource_options({ networks: [{id: 'network1-id', name: 'network1-id'}], }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with connectedoptions set', function(){ $scope.resource = {network: 'network-id',} $scope.connectedoptions = {network: [{value: 'network-id'}]}; element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Basic"); }); it('allocation_pool should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_allocation_pool(); expect($scope.resource.allocation_pools.length).toEqual(2); }); it('allocation_pool should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_allocation_pool(); expect($scope.resource.allocation_pools.length).toEqual(0); }); it('hostroute should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_hostroute(); expect($scope.resource.host_routes.length).toEqual(2); }); it('hostroute should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_hostroute(); expect($scope.resource.host_routes.length).toEqual(0); }); it('validate dns', function() { expect( $isolateScope.validate_dns('@')).toEqual(null); expect( $isolateScope.validate_dns('8.8.8.8')).toEqual('8.8.8.8'); }); }); })(); ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000611715257164523032614 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000024500000000000010216 xustar00137 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/ 28 mtime=1790765446.4082596 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-blue.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000612015257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-gray.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000612015257164523032606 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030000000000000010206 xustar00164 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-green.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000611315257164523032610 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-red.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000611715257164523032614 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027300000000000010217 xustar00159 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.html 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000570715257164523032620 0ustar00zuulzuul
    You must supply a name.
    That doesn't look like a valid name.
    Too long name.
    That doesn't look like a valid public key.
    Too long name.
    save private key
    That doesn't look like a valid user.
    ././@PaxHeader0000000000000000000000000000027100000000000010215 xustar00157 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.js 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000526415257164523032616 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Nova::KeyPair angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNovaKeypairSettings', { resource_key: "OS__Nova__KeyPair", admin: false, icon: { class: 'fa-key ', name: 'OS::Nova::KeyPair', code: '\uf084', color: '#483dff' }, label: 'name', outputs: [ {'property': 'private_key', } ], modal_component: '', edge_settings: null, necessary_properties: { 'name': null }, } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNovaKeypairSettings', 'hotgenGlobals', function( osNovaKeypairSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNovaKeypairSettings.resource_key, osNovaKeypairSettings.icon); hotgenGlobals.update_resource_components( osNovaKeypairSettings.resource_key, osNovaKeypairSettings.modal_component); hotgenGlobals.update_node_labels( osNovaKeypairSettings.resource_key, osNovaKeypairSettings.label); hotgenGlobals.set_resource_outputs( osNovaKeypairSettings.resource_key, osNovaKeypairSettings.outputs); }]); function osNovaKeypairController($scope, hotgenGlobals, validationRules){ $scope.options = hotgenGlobals.get_resource_options(); $scope.admin = $scope.options.auth.admin; this.$onInit = function(){ $scope.dependson = this.dependson; } $scope.validate_name = validationRules['name']; $scope.validate_keypair = validationRules['keypair']; } osNovaKeypairController.$inject = ['$scope', 'hotgenGlobals', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNovaKeypairPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNovaKeypairPath(basePath){ return basePath + 'js/resources/os__nova__keypair/os__nova__keypair.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNovaKeypair', { templateUrl: osNovaKeypairPath, controller: osNovaKeypairController, bindings:{ 'keypair': '=', 'dependson': '=', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.spec.js 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000230215257164523032604 0ustar00zuulzuul(function() { 'use strict'; describe('component os-nova-keypair', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.dependson = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''+ ''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); }); })(); ././@PaxHeader0000000000000000000000000000027200000000000010216 xustar00158 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000611315257164523032610 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000024400000000000010215 xustar00136 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/ 28 mtime=1790765446.4102595 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-blue.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000400115257164523032602 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027500000000000010221 xustar00161 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-gray.svg 28 mtime=1790765394.5604959 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000377315257164523032621 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000010222 xustar00162 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-green.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000377515257164523032623 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027400000000000010220 xustar00160 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-red.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000400115257164523032602 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000027100000000000010215 xustar00157 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.html 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000006633515257164523032624 0ustar00zuulzuul
    That doesn't look like a valid instance name.
    Too long instance name.
    {$ flavor.name $}
    You must supply a flavor.
    {$ source.name $} {$ image.name $} {$ iss.name $} {$ vol.name $} {$ volss.name $} {$ az.name $} {$ 'Show More Properties' | translate $}
    {$ kname.name $} {$ sg.name $} Use block_device_mapping_v2
    {$ dtype.name $} {$ diskbus.name $}
    {$ eformat.name $} Delete On Termination
    {$ source.name $}
    {$ image.name $} {$ vol.name $} {$ volss.name $}
    {$ alloc_net.name $} network subnet port floating_ip
    {$ nw.name $} {$ subnet.name $} {$ port.name $} {$ fip.id $}
    Please input a valid IP address.
    Show Password
    {$ fup.name $} (Default) {$ iup.name $} (Default)
    That doesn't look like a valid uuid.
    Config Drive {$ dc.name $} (Default) {$ udf.name $} (Default) {$ sct.name $} (Default) {$ udup.name $} (Default)
    ././@PaxHeader0000000000000000000000000000026700000000000010222 xustar00155 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.js 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000006510015257164523032611 0ustar00zuulzuul(function(angular) { 'use strict'; // OS::Nova::Server angular.module('horizon.dashboard.project.heat_dashboard.template_generator').value('osNovaServerSettings', { resource_key: "OS__Nova__Server", admin: false, icon: { class: 'fa-desktop ', name: 'OS::Nova::Server', code: '\uf108', color: '#483dff' }, label: 'name', modal_component: '', edge_settings: { 'OS__Cinder__Volume': { 'type': 'property', 'property': 'block_device_mapping_v2.volume_id', 'limit': 99, 'occupied': true, 'lonely': true, }, 'OS__Nova__KeyPair': { 'type': 'property', 'property': 'key_name', 'limit': 1, }, 'OS__Neutron__Net': { 'type': 'property', 'property': 'networks.network', 'limit': 99, }, 'OS__Neutron__FloatingIP': { 'type': 'property', 'property': 'networks.floating_ip', 'limit': 99, }, 'OS__Neutron__Subnet': { 'type': 'property', 'property': 'networks.subnet', 'limit': 99, }, 'OS__Neutron__Port': { 'type': 'property', 'property': 'networks.port', 'limit': 99, }, 'OS__Neutron__SecurityGroup': { 'type': 'property', 'property': 'security_groups', 'limit': 99, }, }, necessary_properties: { 'flavor': null } } ); angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osNovaServerSettings', 'hotgenGlobals', function(osNovaServerSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osNovaServerSettings.resource_key, osNovaServerSettings.icon); hotgenGlobals.update_node_labels( osNovaServerSettings.resource_key, osNovaServerSettings.label); hotgenGlobals.update_resource_components( osNovaServerSettings.resource_key, osNovaServerSettings.modal_component); // for (var i in osNovaServerSettings.edge_settings){ // if (osNovaServerSettings.edge_settings[i].modal){ // hotgenGlobals.update_resource_components( // osNovaServerSettings.resource_key+'_'+i, // osNovaServerSettings.edge_settings[i].modal); // } // } hotgenGlobals.update_edge_directions( osNovaServerSettings.resource_key, osNovaServerSettings.edge_settings); }]); function osNovaServerController($scope, hotgenGlobals, hotgenNotify, hotgenUtils, validationRules) { this.$onInit = function(){ if (typeof this.connectedoptions === 'undefined'){ $scope.connected_options = [] } else{ $scope.connected_options = this.connectedoptions; } if (typeof this.instance.metadata === 'undefined'){ this.instance.metadata = [{}]; } if (typeof this.instance.tags === 'undefined'){ this.instance.tags = []; } if (typeof this.instance.scheduler_hints === 'undefined'){ this.instance.scheduler_hints = [{}]; } if (typeof this.instance.personality === 'undefined'){ this.instance.personality = [{}]; } if (typeof this.instance.block_device_mapping === 'undefined'){ this.instance.block_device_mapping = []; } if (typeof this.instance.security_groups === 'undefined'){ this.instance.security_groups = []; } if (typeof this.instance.block_device_mapping_v2 === 'undefined'){ this.instance.block_device_mapping_v2 = []; } if (typeof this.instance.networks === 'undefined'){ this.instance.networks = []; } this.disable = { 'key_name': false, 'security_groups': [], 'block_device_mapping_v2.volume_id': [], 'networks': {}, } if (this.instance.image){ $scope.boot_source = 'image'; } else if (this.instance.image_snapshot){ $scope.boot_source = 'image_snapshot'; } else if (this.instance.volume){ $scope.boot_source = 'volume'; } else if (this.instance.volume_snapshot){ $scope.boot_source = 'volume_snapshot'; } if ( $scope.connected_options.key_name && $scope.connected_options.key_name.length > 0){ this.instance['key_name'] = $scope.connected_options.key_name[0].value; this.disable.key_name = true; } var old_array = hotgenUtils.filter_and_return_get_resource_element(this.instance['security_groups']); if ( $scope.connected_options.security_groups && $scope.connected_options.security_groups.length > 0){ for (var idx in $scope.connected_options.security_groups){ this.instance['security_groups'].push($scope.connected_options.security_groups[idx].value); this.disable.security_groups.push($scope.connected_options.security_groups[idx].value); } } var old_array = hotgenUtils.filter_and_return_get_resource_element(this.instance['block_device_mapping_v2'], 'volume_id'); var exist_volume_map = {} for (var idx in old_array){ if (old_array[idx].volume_id && old_array[idx].volume_id.length > 0 ){ exist_volume_map[old_array[idx].volume_id] = old_array[idx]; } } this.disable.length = 0; $scope.bdpv2_source = {}; if ( $scope.connected_options['block_device_mapping_v2.volume_id'] && $scope.connected_options['block_device_mapping_v2.volume_id'].length > 0){ for (var idx in $scope.connected_options['block_device_mapping_v2.volume_id']){ var connected_vol_id = $scope.connected_options['block_device_mapping_v2.volume_id'][idx].value; var item = exist_volume_map[connected_vol_id]; if (item == null){ item = {volume_id: connected_vol_id} } this.instance.block_device_mapping_v2.push(item); this.disable['block_device_mapping_v2.volume_id'].push(connected_vol_id); } } for (var idx in this.instance.block_device_mapping_v2){ var disk = this.instance.block_device_mapping_v2[idx]; var source = ''; if (disk.volume_id && disk.volume_id != '') { source = 'volume'; } else if (disk.image && disk.image != '') { source = 'image'; } else if (disk.snapshot_id && disk.snapshot_id != '') { source = 'volume_snapshot'; } $scope.bdpv2_source[idx.toString()] = source; } var old_net_array = hotgenUtils.filter_and_return_get_resource_element(this.instance['networks'], 'network'); var exist_network_map = {} for (var idx in old_net_array){ if (old_net_array[idx].network && old_net_array[idx].network.length > 0 ){ exist_network_map[old_net_array[idx].network] = old_net_array[idx]; } } var old_subnet_array = hotgenUtils.filter_and_return_get_resource_element(this.instance['networks'], 'subnet'); var exist_subnet_map = {} for (var idx in old_subnet_array){ if (old_subnet_array[idx].subnet && old_subnet_array[idx].subnet.length > 0 ){ exist_subnet_map[old_subnet_array[idx].subnet] = old_subnet_array[idx]; } } var old_port_array = hotgenUtils.filter_and_return_get_resource_element(this.instance['networks'], 'port'); var exist_port_map = {} for (var idx in old_port_array){ if (old_port_array[idx].port && old_port_array[idx].port.length > 0 ){ exist_port_map[old_port_array[idx].port] = old_port_array[idx]; } } var old_fip_array = hotgenUtils.filter_and_return_get_resource_element(this.instance['networks'], 'floating_ip'); var exist_fip_map = {} for (var idx in old_fip_array){ if (old_fip_array[idx].floating_ip && old_fip_array[idx].floating_ip.length > 0 ){ exist_fip_map[old_fip_array[idx].floating_ip] = old_fip_array[idx]; } } if ( $scope.connected_options['networks.network'] && $scope.connected_options['networks.network'].length > 0){ for (var idx in $scope.connected_options['networks.network']){ var connected_nw_id = $scope.connected_options['networks.network'][idx].value; var item = exist_network_map[connected_nw_id]; if (item == null){ item = {network: connected_nw_id} } this.disable.networks[this.instance.networks.length.toString()] = true; $scope.how2config_networks[this.instance.networks.length.toString()] = 'network'; this.instance.networks.push(item); } } if ( $scope.connected_options['networks.subnet'] && $scope.connected_options['networks.subnet'].length > 0){ for (var idx in $scope.connected_options['networks.subnet']){ var connected_subnet_id = $scope.connected_options['networks.subnet'][idx].value; var item = exist_subnet_map[connected_subnet_id]; if (item == null){ item = {subnet: connected_subnet_id} } this.disable.networks[this.instance.networks.length] = true; $scope.how2config_networks[this.instance.networks.length.toString()] = 'subnet'; this.instance.networks.push(item); } } if ( $scope.connected_options['networks.port'] && $scope.connected_options['networks.port'].length > 0){ for (var idx in $scope.connected_options['networks.port']){ var connected_port_id = $scope.connected_options['networks.port'][idx].value; var item = exist_port_map[connected_port_id]; if (item == null){ item = {port: connected_port_id} } this.disable.networks[this.instance.networks.length] = true; $scope.how2config_networks[this.instance.networks.length.toString()] = 'port'; this.instance.networks.push(item); } } if ( $scope.connected_options['networks.floating_ip'] && $scope.connected_options['networks.floating_ip'].length > 0){ for (var idx in $scope.connected_options['networks.floating_ip']){ var connected_fip_id = $scope.connected_options['networks.floating_ip'][idx].value; var item = exist_fip_map[connected_fip_id]; if (item == null){ item = {floating_ip: connected_fip_id} } this.disable.networks[this.instance.networks.length] = true; $scope.how2config_networks[this.instance.networks.length.toString()] = 'floating_ip'; this.instance.networks.push(item); } } for (var idx in this.instance.networks){ var netconfig = this.instance.networks[idx]; var source = ''; if (netconfig.network && netconfig.network != '') { source = 'network'; } else if (netconfig.subnet && netconfig.subnet != '') { source = 'subnet'; } else if (netconfig.port && netconfig.port != '') { source = 'port'; } else if (netconfig.floating_ip && netconfig.floating_ip != '') { source = 'floating_ip'; } $scope.how2config_networks[idx.toString()] = source; } $scope.update = { keypairs: $scope.get_keypairs_options(), networks: $scope.get_networks_options(), subnets: $scope.get_subnets_options(), floatingips: $scope.get_floatingips_options(), ports: $scope.get_ports_options(), security_groups: $scope.get_security_groups_options(), volumes: $scope.get_volumes_options(), } $scope.dependson = this.dependson; } $scope.show_passwd = false; $scope.show_passwd_type = "password"; $scope.bdpv2_source = {}; // Mark the source selected of every block_device_mapping_v2 item. $scope.how2config_networks = {} $scope.update_boot_source = function(){ if ($scope.boot_source == 'image'){ this.$ctrl.instance.image_snapshot = null; this.$ctrl.instance.volume = null; this.$ctrl.instance.volume_snapshot = null; } else if ($scope.boot_source == 'image_snapshot'){ this.$ctrl.instance.image = null; this.$ctrl.instance.volume = null; this.$ctrl.instance.volume_snapshot = null; } else if ($scope.boot_source == 'volume'){ this.$ctrl.instance.image = null; this.$ctrl.instance.image_snapshot = null; this.$ctrl.instance.volume_snapshot = null; } else if ($scope.boot_source == 'volume_snapshot'){ this.$ctrl.instance.image = null; this.$ctrl.instance.image_snapshot = null; this.$ctrl.instance.volume = null; } } $scope.update_source = function (index) { if ($scope.bdpv2_source[index] == 'volume'){ this.$ctrl.instance.block_device_mapping_v2[index].image = null; this.$ctrl.instance.block_device_mapping_v2[index].snapshot_id = null; } else if ($scope.bdpv2_source[index] == 'volume_snapshot'){ this.$ctrl.instance.block_device_mapping_v2[index].volume_id = null; this.$ctrl.instance.block_device_mapping_v2[index].image = null; } else if ($scope.bdpv2_source[index] == 'image'){ this.$ctrl.instance.block_device_mapping_v2[index].volume_id = null; this.$ctrl.instance.block_device_mapping_v2[index].snapshot_id = null; } } $scope.update_nwconfig = function (index) { if ($scope.how2config_networks[index] == 'network'){ this.$ctrl.instance.networks[index].subnet = null; this.$ctrl.instance.networks[index].port = null; this.$ctrl.instance.networks[index].floating_ip = null; } else if ($scope.how2config_networks[index] == 'subnet'){ this.$ctrl.instance.networks[index].network = null; this.$ctrl.instance.networks[index].port = null; this.$ctrl.instance.networks[index].floating_ip = null; } else if ($scope.how2config_networks[index] == 'port'){ this.$ctrl.instance.networks[index].subnet = null; this.$ctrl.instance.networks[index].network = null; this.$ctrl.instance.networks[index].floating_ip = null; } else if ($scope.how2config_networks[index] == 'floating_ip'){ this.$ctrl.instance.networks[index].subnet = null; this.$ctrl.instance.networks[index].port = null; this.$ctrl.instance.networks[index].network = null; } } $scope.$watch("show_passwd", function(newValue, oldValue) { $scope.show_passwd_type = $scope.show_passwd ? "text" : "password"; }); $scope.options = hotgenGlobals.get_resource_options(); $scope.get_security_groups_options = function(){ if ('security_groups' in $scope.connected_options){ var resource_secgroups = []; for (var idx in $scope.connected_options.security_groups){ var item = $scope.connected_options.security_groups[idx]; resource_secgroups.push({ id: item.value, name: item.value }) } return $scope.options.security_groups.concat(resource_secgroups); } return $scope.options.security_groups; } $scope.get_volumes_options = function(){ if ('block_device_mapping_v2.volume_id' in $scope.connected_options){ var resource_volumes = []; for (var idx in $scope.connected_options['block_device_mapping_v2.volume_id']){ var item = $scope.connected_options['block_device_mapping_v2.volume_id'][idx]; resource_volumes.push({ id: item.value, name: item.value }) } return $scope.options.volumes.concat(resource_volumes); } return $scope.options.volumes; } $scope.get_keypairs_options = function(){ if ('key_name' in $scope.connected_options){ var resource_keypair = []; for (var idx in $scope.connected_options.key_name){ var item = $scope.connected_options.key_name[idx]; resource_keypair.push({ name: item.value }) } return $scope.options.keypairs.concat(resource_keypair); } return $scope.options.keypairs; } $scope.get_networks_options = function(){ if ('networks.network' in $scope.connected_options){ var resource_nw = []; for (var idx in $scope.connected_options['networks.network']){ var item = $scope.connected_options['networks.network'][idx]; resource_nw.push({ id: item.value, name: item.value }) } return $scope.options.networks.concat(resource_nw); } return $scope.options.networks; } $scope.get_subnets_options = function(){ if ('networks.subnet' in $scope.connected_options){ var resource_subnet = []; for (var idx in $scope.connected_options['networks.subnet']){ var item = $scope.connected_options['networks.subnet'][idx]; resource_subnet.push({ id: item.value, name: item.value }) } return $scope.options.subnets.concat(resource_subnet); } return $scope.options.subnets; } $scope.get_floatingips_options = function(){ if ('networks.floating_ip' in $scope.connected_options){ var resource_fip = []; for (var idx in $scope.connected_options['networks.floating_ip']){ var item = $scope.connected_options['networks.floating_ip'][idx]; resource_fip.push({ id: item.value, }) } return $scope.options.floatingips.concat(resource_fip); } return $scope.options.floatingips; } $scope.get_ports_options = function(){ if ('networks.port' in $scope.connected_options){ var resource_port = []; for (var idx in $scope.connected_options['networks.port']){ var item = $scope.connected_options['networks.port'][idx]; resource_port.push({ id: item.value, name: item.value }) } return $scope.options.ports.concat(resource_port); } return $scope.options.ports; } $scope.block_device_mapping_v2 = true; $scope.deployment_swift_data = {}; $scope.show_more = false; $scope.options.boot_sources = [ {'id': 'image', 'name': 'image'}, {'id': 'image_snapshot', 'name': 'image snapshot'}, {'id': 'volume', 'name': 'volume'}, {'id': 'volume_snapshot', 'name': 'volume snapshot'} ]; $scope.options.volume_sources = [ {'id': 'volume', 'name': 'volume'}, {'id': 'volume_snapshot', 'name': 'volume snapshot'} ]; $scope.options.volume_sources_v2 = [ {'id': 'image', 'name': 'image'}, {'id': 'volume', 'name': 'volume'}, {'id': 'volume_snapshot', 'name': 'volume snapshot'} ]; $scope.options.flavor_update_policies = [ {'name': 'RESIZE', 'default': true}, {'name': 'REPLACE'}, ]; $scope.options.image_update_policies = [ {'name': 'REBUILD', 'default': true}, {'name': 'REPLACE'}, {'name': 'REBUILD_PRESERVE_EPHEMERAL'}, ]; $scope.options.disk_configs = [ {'name': 'AUTO', 'default': true}, {'name': 'MANUAL'}, ]; $scope.options.software_config_transports = [ {'name': 'POLL_SERVER_CFN', 'default': true}, {'name': 'POLL_SERVER_HEAT'}, {'name': 'POLL_TEMP_URL'}, {'name': 'ZAQAR_MESSAGE'}, ]; $scope.options.user_data_update_policies = [ {'name': 'REPLACE', 'default': true}, {'name': 'IGNORE'}, ]; $scope.options.user_data_formats = [ {'name': 'HEAT_CFNTOOLS', 'default': true}, {'name': 'RAW'}, {'name': 'SOFTWARE_CONFIG'} ]; $scope.options.disk_types = [ {'name': 'disk'}, {'name': 'cdrom'}, ] $scope.options.disk_buses = [ {'name': 'ide'}, {'name': 'lame_bus'}, {'name': 'scsi'}, {'name': 'usb'}, {'name': 'virtio'}, ]; $scope.options.ephemeral_formats = [ {'name': 'ext2'}, {'name': 'ext3'}, {'name': 'ext4'}, {'name': 'xfs'}, {'name': 'ntfs'}, ]; $scope.options.allocate_networks = [{'name': 'none'}, {'name': 'auto'}]; $scope.validate_name = validationRules['name']; $scope.validate_ip_address = validationRules['ip_address']; $scope.validate_uuid4 = validationRules['uuid4']; this.delete_metadata = function(index){ this.instance.metadata.splice(index, 1) } this.add_metadata = function(){ this.instance.metadata.push({}) } this.delete_personality = function(index){ this.instance.personality.splice(index, 1) } this.add_personality = function(){ this.instance.personality.push({}) } this.delete_block_device_mapping = function(index){ this.instance.block_device_mapping.splice(index, 1) } this.add_block_device_mapping = function(){ this.instance.block_device_mapping.push({}) } this.delete_block_device_mapping_v2 = function(index){ for (var i = index; i <= this.instance.block_device_mapping_v2.length; i=i+1){ $scope.bdpv2_source[i] = $scope.bdpv2_source[i+1]; } this.instance.block_device_mapping_v2.splice(index, 1) } this.add_block_device_mapping_v2 = function(){ this.instance.block_device_mapping_v2.push({}) } this.delete_networks = function(index){ for (var i = index; i < this.instance.networks.length; i=i+1){ $scope.how2config_networks[i] = $scope.how2config_networks[i+1]; } delete $scope.how2config_networks[this.instance.networks.length]; this.instance.networks.splice(index, 1); for (var i = index; i < this.instance.networks.length; i=i+1){ this.disable.networks[i] = this.disable.networks[i+1]; } delete this.disable.networks[this.instance.networks.length]; } this.add_networks = function(){ this.instance.networks.push({}) } this.delete_scheduler_hints = function(index){ this.instance.scheduler_hints.splice(index, 1) } this.add_scheduler_hints= function(){ this.instance.scheduler_hints.push({}) } } osNovaServerController.$inject = ['$scope', 'hotgenGlobals', 'hotgenNotify', 'hotgenUtils', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osNovaServerPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; function osNovaServerPath(basePath){ return basePath + 'js/resources/os__nova__server/os__nova__server.html'; } angular.module('horizon.dashboard.project.heat_dashboard.template_generator').component('osNovaServer', { templateUrl: osNovaServerPath, controller: osNovaServerController, bindings:{ 'instance': '=', 'dependson': '=', 'connectedoptions': '<', 'formReference': '<', } }); })(window.angular); ././@PaxHeader0000000000000000000000000000027400000000000010220 xustar00160 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.spec.js 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000003175015257164523032615 0ustar00zuulzuul(function() { 'use strict'; describe('component os-nova-server', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var hotgenGlobals, hotgenUtils; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); hotgenGlobals = $injector.get('hotgenGlobals'); hotgenUtils = $injector.get('hotgenUtils'); hotgenGlobals.update_resource_options({ networks: [{id: 'network1-id', name: 'network1-id'}], subnets: [{id: 'subnet1-id', name: 'subnet1-id'}], ports: [{id: 'port1-id', name: 'port1-id'}], volumes: [{id: 'volume1-id', name: 'volume1-id'}], floatingips: [{id: 'ipaddress', name: 'ipaddress'}], security_groups: [{id: 'secgroup-id1', name: 'secgroup-id1'}], keypairs: [{id: 'keyname1', name: 'keyname1'}], }); $scope.resource = {}; $scope.dependson = []; $scope.connectedoptions = []; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with no connectedoptions set', function() { element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with connectedoptions set', function(){ $scope.resource = {image: 'image-id', key_name: 'keyname', security_groups: ['secgroup-01'], block_device_mapping_v2: [ {volume_id: '{ get_resource: vol01 }'}, {image: 'image-id'}, {image: '{ get_resource: image01 }'}, {snapshot_id: 'snapshot-id'}, {ignore: 'ignore'} ], networks: [{network: '{ get_resource: network01 }'}, {subnet: '{ get_resource: subnet01 }'}, {port: '{ get_resource: port01 }'}, {floating_ip: '{ get_resource: floatingip01 }'}, {ignore: 'ignore'} ], } $scope.connectedoptions = { 'networks.network': [{value: 'network-id'}, {value: '{ get_resource: network01 }'}], 'networks.subnet': [{value: 'subnet-id'}, {value: '{ get_resource: subnet01 }'}], 'networks.port': [{value: 'port-id'}, {value: '{ get_resource: port01 }'}], 'networks.floating_ip': [{value: 'floatingip'}, {value: '{ get_resource: floatingip01 }'}], key_name: 'keyname', security_groups: [{value: 'secgroup-id'}], 'block_device_mapping_v2.volume_id': [{value: 'volume-id'}, {value: '{ get_resource: vol01 }'}], } element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); $isolateScope.update_boot_source(); for (var i in $scope.resource.block_device_mapping_v2){ $isolateScope.update_source(i); } for (var i in $scope.resource.networks){ $isolateScope.update_nwconfig(i); } expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with connectedoptions set with error', function(){ $scope.resource = {image: 'image-id', key_name: 'keyname', security_groups: ['secgroup-01'], block_device_mapping_v2: [ {volume_id: '{ get_resource: vol01 }'}, {image: 'image-id'}, {image: '{ get_resource: image01 }'}, {snapshot_id: 'snapshot-id'}, {ignore: 'ignore'} ], networks: [{network: '{ get_resource: network01 }'}, {subnet: '{ get_resource: subnet01 }'}, {port: '{ get_resource: port01 }'}, {floating_ip: '{ get_resource: floatingip01 }'} ], } $scope.connectedoptions = { 'networks.network': [{value: 'network-id'}, {value: '{ get_resource: network01 }'}], 'networks.subnet': [{value: 'subnet-id'}, {value: '{ get_resource: subnet01 }'}], 'networks.port': [{value: 'port-id'}, {value: '{ get_resource: port01 }'}], 'networks.floating_ip': [{value: 'floatingip'}, {value: '{ get_resource: floatingip01 }'}], key_name: 'keyname', security_groups: [{value: 'secgroup-id'}], 'block_device_mapping_v2.volume_id': [{value: 'volume-id'}, {value: '{ get_resource: vol01 }'}], } spyOn(hotgenUtils, 'filter_and_return_get_resource_element').and.callFake(function(){ return [{}] }); element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with boot from image snapshot', function(){ $scope.resource = {image_snapshot: 'image-id'} element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); $isolateScope.update_boot_source(); expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with boot from volume ', function(){ $scope.resource = {volume: 'vol-id'} element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); $isolateScope.update_boot_source(); expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with boot from volume snapshot', function(){ $scope.resource = {volume_snapshot: 'vol-id'} element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); $isolateScope.update_boot_source(); expect(element.find('span').html()).toContain("Basic"); }); it('find tab title with boot from nothing ', function(){ $scope.resource = {ignore: 'nothing'} element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); $isolateScope.update_boot_source(); expect(element.find('span').html()).toContain("Basic"); }); it('$scope.show_passwd should be successfully watched', function() { $isolateScope.show_passwd = true; $isolateScope.$digest(); expect($isolateScope.show_passwd_type).toEqual('text'); $isolateScope.show_passwd = false; $isolateScope.$digest(); expect($isolateScope.show_passwd_type).toEqual('password'); }); it('metadata should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_metadata(); expect($scope.resource.metadata.length).toEqual(2); }); it('metadata should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_metadata(); expect($scope.resource.metadata.length).toEqual(0); }); it('personality should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_personality(); expect($scope.resource.personality.length).toEqual(2); }); it('personality should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_personality(); expect($scope.resource.personality.length).toEqual(0); }); it('block_device_mapping should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_block_device_mapping(); expect($scope.resource.block_device_mapping.length).toEqual(1); }); it('block_device_mapping should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_block_device_mapping(); $ctrl.delete_block_device_mapping(0); expect($scope.resource.block_device_mapping.length).toEqual(0); }); it('block_device_mapping_v2 should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_block_device_mapping_v2(); expect($scope.resource.block_device_mapping_v2.length).toEqual(1); }); it('block_device_mapping_v2 should be successfully deleted', function() { $scope.resource = {block_device_mapping_v2: [{volume_id: 'vol-01'}]} element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); var $ctrl = $isolateScope.$ctrl; $ctrl.delete_block_device_mapping_v2(0); expect($scope.resource.block_device_mapping_v2.length).toEqual(0); }); it('networks should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_networks(); expect($scope.resource.networks.length).toEqual(1); }); it('networks should be successfully deleted', function() { $scope.resource = {networks: [{network: 'network-01'}]}; element = $compile(angular.element(''))($scope); $scope.$digest(); $isolateScope = element.isolateScope(); var $ctrl = $isolateScope.$ctrl; $ctrl.delete_networks(0); expect($scope.resource.networks.length).toEqual(0); }); it('scheduler_hints should be successfully added', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.add_scheduler_hints(); expect($scope.resource.scheduler_hints.length).toEqual(2); }); it('scheduler_hints should be successfully deleted', function() { var $ctrl = $isolateScope.$ctrl; $ctrl.delete_scheduler_hints(); expect($scope.resource.scheduler_hints.length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000027000000000000010214 xustar00156 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000377515257164523032623 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000025000000000000010212 xustar00140 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/ 28 mtime=1790765446.4122596 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000775000175000017500000000000015257164606032607 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000030500000000000010213 xustar00169 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-blue.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000364315257164523032615 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030500000000000010213 xustar00169 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-gray.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000363515257164523032616 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030600000000000010214 xustar00170 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-green.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000363715257164523032620 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030400000000000010212 xustar00168 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-red.svg 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000364315257164523032615 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000030100000000000010207 xustar00165 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.html 28 mtime=1790765394.5614958 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000703015257164523032607 0ustar00zuulzuul
    That doesn't look like a valid container name.
    Your container name is too long.
    Purge on Delete
    ././@PaxHeader0000000000000000000000000000027700000000000010223 xustar00163 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.js 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000736615257164523032623 0ustar00zuulzuul(function() { 'use strict'; var c_meta_key = "X-Container-Meta", a_meta_key = "X-Account-Meta", c_read_key = "X-Container-Read", c_write_key = "X-Container-Write", purge_key = "PurgeOnDelete"; /** * OS::Swift::Container */ angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .value('osSwiftContainerSettings', { resource_key: "OS__Swift__Container", admin: false, icon: { class: 'fa-archive ', name: 'OS::Swift::Container', code: '\uf0a0', color: '#0bb238' }, label: 'name', modal_component: '', edge_settings: null, necessary_properties: { name: null } } ) // Register the resource to globals angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .run(['osSwiftContainerSettings','hotgenGlobals', function(osSwiftContainerSettings, hotgenGlobals){ hotgenGlobals.update_resource_icons( osSwiftContainerSettings.resource_key , osSwiftContainerSettings.icon); hotgenGlobals.update_resource_components( osSwiftContainerSettings.resource_key, osSwiftContainerSettings.modal_component); hotgenGlobals.update_node_labels( osSwiftContainerSettings.resource_key, osSwiftContainerSettings.label); }]); // Define controller function osSwiftContainerController($scope, hotgenGlobals, hotgenNotify, validationRules) { this.$onInit = function(){ // Initialize X-Container-Meta if (typeof this.container[c_meta_key] === 'undefined'){ this.container[c_meta_key] = [{}]; } // Initialize X-Account-Meta if (typeof this.container[a_meta_key] === 'undefined'){ this.container[a_meta_key] = [{}]; } // Intialize Purge On Delete if (typeof this.container[purge_key] === 'undefined'){ this.container[purge_key] = false; } }; $scope.options = hotgenGlobals.get_resource_options(); $scope.show_more = false; $scope.validate_name = validationRules['name']; // Container Metadata manipulation functions this.add_x_container_meta = function(){ this.container[c_meta_key].push({}) } this.delete_x_container_meta = function(index){ this.container[c_meta_key].splice(index, 1) } // Account Metadata manipulation functions this.add_x_account_meta = function(){ this.container[a_meta_key].push({}) } this.delete_x_account_meta = function(index){ this.container[a_meta_key].splice(index, 1) } } function osSwiftContainerPath (basePath){ return basePath + 'js/resources/os__swift__container/os__swift__container.html'; } osSwiftContainerController.$inject = [ '$scope', 'hotgenGlobals', 'hotgenNotify', 'horizon.dashboard.project.heat_dashboard.template_generator.validationRules', ]; osSwiftContainerPath.$inject = ['horizon.dashboard.project.heat_dashboard.template_generator.basePath']; angular.module('horizon.dashboard.project.heat_dashboard.template_generator') .component('osSwiftContainer', { templateUrl: osSwiftContainerPath, controller: osSwiftContainerController, bindings: { 'container': '=', 'formReference': '<', } }); })(); ././@PaxHeader0000000000000000000000000000030400000000000010212 xustar00168 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.spec.js 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000543315257164523032614 0ustar00zuulzuul(function() { 'use strict'; describe('component os-swift-container', function(){ beforeEach(module('horizon.dashboard.project.heat_dashboard.template_generator')); beforeEach(module('appTemplates')); var $scope, $isolateScope, $compile; var element; beforeEach(inject(function($injector) { $scope = $injector.get('$rootScope').$new(); $compile = $injector.get('$compile'); $scope.resource = {}; $scope.resourceForm = {}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); })); it('find tab title Properties', function() { expect(element.find('span').html()).toContain("Properties"); }); it('find tab title with resource set', function() { $scope.resource = {metadata: [], scheduler_hints:[]}; // element will enable you to test your directive's element on the DOM element = $compile(angular.element(''))($scope); // Digest needs to be called to set any values on the directive's scope $scope.$digest(); $isolateScope = element.isolateScope(); expect(element.find('span').html()).toContain("Properties"); }); // X-Container-Meta it('x-container-meta should be successfully added', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.add_x_container_meta(); expect($scope.resource['X-Container-Meta'].length).toEqual(2); }); it('x-container-meta should be successfully deleted', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.delete_x_container_meta(); expect($scope.resource['X-Container-Meta'].length).toEqual(0); }); // X-Account-Meta it('x-account-meta should be successfully added', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.add_x_account_meta(); expect($scope.resource['X-Account-Meta'].length).toEqual(2); }); it('x-account-meta should be successfully deleted', function() { var $ctrl = element.isolateScope().$ctrl; $ctrl.delete_x_account_meta(); expect($scope.resource['X-Account-Meta'].length).toEqual(0); }); }); })(); ././@PaxHeader0000000000000000000000000000030000000000000010206 xustar00164 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.svg 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/r0000664000175000017500000000363715257164523032620 0ustar00zuulzuul Layer 1 ././@PaxHeader0000000000000000000000000000022000000000000010207 xustar00116 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/ 28 mtime=1790765446.4132595 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/temp0000775000175000017500000000000015257164606032677 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000023700000000000010217 xustar00131 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/depends_on.html 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/temp0000664000175000017500000000135315257164523032701 0ustar00zuulzuul Check following resources to specify the depends_on relations.
    {{ node.label }}
    ././@PaxHeader0000000000000000000000000000024000000000000010211 xustar00132 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_draft.html 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/temp0000664000175000017500000000217515257164523032704 0ustar00zuulzuul

    {$ 'Saved Drafts' | translate $}

    Last saved draft is {$ latest_draft.time $}. You haven't saved any draft yet! {$ draft.time $}
    {$ 'OK' | translate $}
    ././@PaxHeader0000000000000000000000000000023700000000000010217 xustar00131 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_edge.html 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/temp0000664000175000017500000000405415257164523032702 0ustar00zuulzuul

    Relations - depends on

    FROM

    {$ from_type $}

    {$ from_node.id $}

    TO

    {$ to_type $}

    {$ to_node.id $}

    {$ 'Delete Resource' | translate $} {$ 'OK' | translate $}
    ././@PaxHeader0000000000000000000000000000024300000000000010214 xustar00135 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_resource.html 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/temp0000664000175000017500000000233115257164523032676 0ustar00zuulzuul

    Resource {$ resource_type $}

    {$ 'Delete Resource' | translate $} {$ 'Cancel' | translate $} {$ 'Save' | translate $}
    ././@PaxHeader0000000000000000000000000000024300000000000010214 xustar00135 path=heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_template.html 28 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/temp0000664000175000017500000000320115257164523032673 0ustar00zuulzuul

    {$ 'Heat Orchestration Template' | translate $}

    {$ warning | translate $}
    {$ 'Cancel' | translate $} {$ 'Download' | translate $} {$ 'Create Stack' | translate $}
    ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.4142594 heat_dashboard-16.0.0/heat_dashboard/test/0000775000175000017500000000000015257164606017273 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5624957 heat_dashboard-16.0.0/heat_dashboard/test/__init__.py0000664000175000017500000000000015257164523021370 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6634953 heat_dashboard-16.0.0/heat_dashboard/test/helpers.py0000664000175000017500000003524615257164523021317 0ustar00zuulzuul# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, 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 importlib import import_module import os import traceback import unittest from unittest import mock from urllib import parse from django.conf import settings from django.contrib.messages.storage import default_storage from django.core.handlers import wsgi from django.test.client import RequestFactory from heatclient import client as heat_client from keystoneclient.v2_0 import client as keystone_client from openstack_auth import user from openstack_auth import utils from requests.packages.urllib3.connection import HTTPConnection from horizon.test import helpers as horizon_helpers from openstack_dashboard import api as project_api from openstack_dashboard import context_processors from openstack_dashboard.test import helpers from heat_dashboard import api from heat_dashboard.test.test_data import utils as test_utils # Makes output of failing mox tests much easier to read. wsgi.WSGIRequest.__repr__ = lambda self: "" # Shortcuts to avoid importing openstack_dashboard.test.helper and # for backwards compatibility. create_mocks = helpers.create_mocks IsA = helpers.IsA IsHttpRequest = helpers.IsHttpRequest def _apply_panel_mocks(patchers=None): """Global mocks on panels that get called on all views.""" if patchers is None: patchers = {} mocked_methods = getattr(settings, 'TEST_GLOBAL_MOCKS_ON_PANELS', {}) for name, mock_config in mocked_methods.items(): method = mock_config['method'] mock_params = {} for param in ['return_value', 'side_effect']: if param in mock_config: mock_params[param] = mock_config[param] patcher = mock.patch(method, **mock_params) patcher.start() patchers[name] = patcher return patchers class RequestFactoryWithMessages(RequestFactory): def get(self, *args, **kwargs): req = super().get(*args, **kwargs) req.user = utils.get_user(req) req.session = [] req._messages = default_storage(req) return req def post(self, *args, **kwargs): req = super().post(*args, **kwargs) req.user = utils.get_user(req) req.session = [] req._messages = default_storage(req) return req @unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False), "The SKIP_UNITTESTS env variable is set.") class TestCase(horizon_helpers.TestCase): """Specialized base test case class for Horizon. It gives access to numerous additional features: * A full suite of test data through various attached objects and managers (e.g. ``self.servers``, ``self.user``, etc.). See the docs for :class:`~openstack_dashboard.test.test_data.utils.TestData` for more information. * The ``mox`` mocking framework via ``self.mox``. * A set of request context data via ``self.context``. * A ``RequestFactory`` class which supports Django's ``contrib.messages`` framework via ``self.factory``. * A ready-to-go request object via ``self.request``. * The ability to override specific time data controls for easier testing. * Several handy additional assertion methods. """ # To force test failures when unmocked API calls are attempted, provide # boolean variable to store failures missing_mocks = False def fake_conn_request(self): # print a stacktrace to illustrate where the unmocked API call # is being made from traceback.print_stack() # forcing a test failure for missing mock self.missing_mocks = True def setUp(self): self._real_conn_request = HTTPConnection.connect HTTPConnection.connect = self.fake_conn_request self._real_context_processor = context_processors.openstack context_processors.openstack = lambda request: self.context self.patchers = _apply_panel_mocks() super().setUp() def _setup_test_data(self): super()._setup_test_data() test_utils.load_test_data(self) self.context = { 'authorized_tenants': self.tenants.list(), 'JS_CATALOG': context_processors.get_js_catalog(settings), } def _setup_factory(self): # For some magical reason we need a copy of this here. self.factory = RequestFactoryWithMessages() def _setup_user(self, **kwargs): self._real_get_user = utils.get_user tenants = self.context['authorized_tenants'] base_kwargs = { 'id': self.user.id, 'token': self.token, 'username': self.user.name, 'domain_id': self.domain.id, 'user_domain_name': self.domain.name, 'tenant_id': self.tenant.id, 'service_catalog': self.service_catalog, 'authorized_tenants': tenants } base_kwargs.update(kwargs) self.setActiveUser(**base_kwargs) def _setup_request(self): super()._setup_request() self.request.session['token'] = self.token.id def tearDown(self): HTTPConnection.connect = self._real_conn_request context_processors.openstack = self._real_context_processor utils.get_user = self._real_get_user mock.patch.stopall() super().tearDown() # cause a test failure if an unmocked API call was attempted if self.missing_mocks: raise AssertionError("An unmocked API call was made.") def setActiveUser(self, id=None, token=None, username=None, tenant_id=None, service_catalog=None, tenant_name=None, roles=None, authorized_tenants=None, enabled=True, domain_id=None, user_domain_name=None): def get_user(request): ret = user.User(id=id, token=token, user=username, domain_id=domain_id, user_domain_name=user_domain_name, tenant_id=tenant_id, tenant_name=tenant_name, service_catalog=service_catalog, roles=roles, enabled=enabled, authorized_tenants=authorized_tenants, endpoint=settings.OPENSTACK_KEYSTONE_URL) ret._is_system_user = False return ret utils.get_user = get_user def assertRedirectsNoFollow(self, response, expected_url): """Check for redirect. Asserts that the given response issued a 302 redirect without processing the view which is redirected to. """ if response.has_header('location'): loc = parse.unquote(response['location']) else: loc = '' expected_url = parse.unquote(expected_url) self.assertEqual(loc, expected_url) self.assertEqual(response.status_code, 302) def assertNoFormErrors(self, response, context_name="form"): """Checks for no form errors. Asserts that the response either does not contain a form in its context, or that if it does, that form has no errors. """ context = getattr(response, "context", {}) if not context or context_name not in context: return True errors = response.context[context_name]._errors assert len(errors) == 0, \ "Unexpected errors were found on the form: %s" % errors def assertFormErrors(self, response, count=0, message=None, context_name="form"): """Check for form errors. Asserts that the response does contain a form in its context, and that form has errors, if count were given, it must match the exact numbers of errors """ context = getattr(response, "context", {}) assert (context and context_name in context), \ "The response did not contain a form." errors = response.context[context_name]._errors if count: assert len(errors) == count, \ "%d errors were found on the form, %d expected" % \ (len(errors), count) if message and message not in str(errors): self.fail( "Expected message not found, instead found: %s" % ["{}: {}".format(key, [e for e in field_errors]) for (key, field_errors) in errors.items()]) else: assert len(errors) > 0, "No errors were found on the form" def assertStatusCode(self, response, expected_code): """Validates an expected status code. Matches camel case of other assert functions """ if response.status_code == expected_code: return self.fail('status code {!r} != {!r}: {}'.format(response.status_code, expected_code, response.content)) def assertItemsCollectionEqual(self, response, items_list): self.assertEqual(response.json, {"items": items_list}) def getAndAssertTableRowAction(self, response, table_name, action_name, row_id): table = response.context[table_name + '_table'] rows = list(filter(lambda x: x.id == row_id, table.data)) self.assertEqual(1, len(rows), "Did not find a row matching id '%s'" % row_id) row_actions = table.get_row_actions(rows[0]) actions = list(filter(lambda x: x.name == action_name, row_actions)) msg_args = (action_name, table_name, row_id) self.assertGreater( len(actions), 0, "No action named '%s' found in '%s' table for id '%s'" % msg_args) self.assertEqual( 1, len(actions), "Multiple actions named '%s' found in '%s' table for id '%s'" % msg_args) return actions[0] def getAndAssertTableAction(self, response, table_name, action_name): table = response.context[table_name + '_table'] table_actions = table.get_table_actions() actions = list(filter(lambda x: x.name == action_name, table_actions)) msg_args = (action_name, table_name) self.assertGreater( len(actions), 0, "No action named '%s' found in '%s' table" % msg_args) self.assertEqual( 1, len(actions), "More than one action named '%s' found in '%s' table" % msg_args) return actions[0] @staticmethod def mock_rest_request(**args): mock_args = { 'user.is_authenticated.return_value': True, 'policy.check.return_value': True, 'body': '' } mock_args.update(args) return mock.Mock(**mock_args) class BaseAdminViewTests(TestCase): """Sets an active user with the "admin" role. For testing admin-only views and functionality. """ def setActiveUser(self, *args, **kwargs): if "roles" not in kwargs: kwargs['roles'] = [self.roles.admin._info] super().setActiveUser(*args, **kwargs) def setSessionValues(self, **kwargs): settings.SESSION_ENGINE = 'django.contrib.sessions.backends.file' engine = import_module(settings.SESSION_ENGINE) store = engine.SessionStore() for key in kwargs: store[key] = kwargs[key] self.request.session[key] = kwargs[key] store.save() self.session = store self.client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key class APITestCase(TestCase): """Testing APIs. For use with tests which deal with the underlying clients rather than stubbing out the openstack_dashboard.api.* methods. """ def setUp(self): super().setUp() utils.patch_middleware_get_user() def fake_keystoneclient(request, admin=False): """Returns the stub keystoneclient. Only necessary because the function takes too many arguments to conveniently be a lambda. """ return self.stub_keystoneclient() # Store the original clients self._original_keystoneclient = project_api.keystone.keystoneclient self._original_heatclient = api.heat.heatclient # Replace the clients with our stubs. project_api.keystone.keystoneclient = fake_keystoneclient api.heat.heatclient = (lambda request, password=None: self.stub_heatclient()) def tearDown(self): super().tearDown() project_api.keystone.keystoneclient = self._original_keystoneclient api.heat.heatclient = self._original_heatclient def stub_keystoneclient(self): if not hasattr(self, "keystoneclient"): keystone_client.Client = mock.Mock() # NOTE(saschpe): Mock properties, MockObject.__init__ ignores them: keystone_client.Client.auth_token = 'foo' keystone_client.Client.service_catalog = None keystone_client.Client.tenant_id = '1' keystone_client.Client.tenant_name = 'tenant_1' keystone_client.Client.management_url = "" keystone_client.Client.__dir__ = lambda: [] self.keystoneclient = keystone_client.Client return self.keystoneclient def stub_heatclient(self): if not hasattr(self, "heatclient"): heat_client.Client = mock.Mock() self.heatclient = heat_client.Client return self.heatclient class RestAPITestCase(TestCase): def setUp(self): super().setUp() mock.patch('horizon.utils.http.is_ajax', return_value=True).start() # Need this to test both Glance API V1 and V2 versions class ResetImageAPIVersionMixin: def setUp(self): super().setUp() project_api.glance.VERSIONS.clear_active_cache() def tearDown(self): project_api.glance.VERSIONS.clear_active_cache() super().tearDown() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.4152594 heat_dashboard-16.0.0/heat_dashboard/test/integration/0000775000175000017500000000000015257164606021616 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5634959 heat_dashboard-16.0.0/heat_dashboard/test/integration/__init__.py0000664000175000017500000000000015257164523023713 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5634959 heat_dashboard-16.0.0/heat_dashboard/test/integration/horizon.conf0000664000175000017500000000032715257164523024155 0ustar00zuulzuul[plugin] is_plugin=True plugin_page_path=heat_dashboard.test.integration.pages plugin_page_structure='{"Project": {"Orchestration": {"_": ["Stacks", "Resource Types", "Template Versions", "Template Generator"]}}}' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.4152594 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/0000775000175000017500000000000015257164606022715 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5634959 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/__init__.py0000664000175000017500000000000015257164523025012 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.4152594 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/0000775000175000017500000000000015257164606024363 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5634959 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/__init__.py0000664000175000017500000000000015257164523026460 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.4172595 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/0000775000175000017500000000000015257164606027247 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5634959 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/__init__.py0000664000175000017500000000000015257164523031344 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000021400000000000010212 xustar00112 path=heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/resourcetypespage.py 28 mtime=1790765394.5634959 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/resourcetypespage.0000664000175000017500000000146715257164523033027 0ustar00zuulzuul# # 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 openstack_dashboard.test.integration_tests.pages import basepage class ResourcetypesPage(basepage.BaseNavigationPage): def __init__(self, driver, conf): super().__init__(driver, conf) self._page_title = "Resource Types" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/stackspage.py0000664000175000017500000000145015257164523031744 0ustar00zuulzuul# # 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 openstack_dashboard.test.integration_tests.pages import basepage class StacksPage(basepage.BaseNavigationPage): def __init__(self, driver, conf): super().__init__(driver, conf) self._page_title = "Stacks" ././@PaxHeader0000000000000000000000000000022000000000000010207 xustar00116 path=heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/templategeneratorpage.py 28 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/templategeneratorp0000664000175000017500000000147715257164523033103 0ustar00zuulzuul# # 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 openstack_dashboard.test.integration_tests.pages import basepage class TemplategeneratorPage(basepage.BaseNavigationPage): def __init__(self, driver, conf): super().__init__(driver, conf) self._page_title = "Template Generator" ././@PaxHeader0000000000000000000000000000021700000000000010215 xustar00115 path=heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/templateversionspage.py 28 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/integration/pages/project/orchestration/templateversionspa0000664000175000017500000000147515257164523033124 0ustar00zuulzuul# # 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 openstack_dashboard.test.integration_tests.pages import basepage class TemplateversionsPage(basepage.BaseNavigationPage): def __init__(self, driver, conf): super().__init__(driver, conf) self._page_title = "Template Versions" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/integration/test_basic.py0000664000175000017500000000346015257164523024311 0ustar00zuulzuul# # 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 openstack_dashboard.test.integration_tests import helpers class TestHeatDashboardInstalled(helpers.TestCase): def test_alarms_page_opened(self): stacks_page = self.home_pg.go_to_project_orchestration_stackspage() self.assertEqual(stacks_page.page_title, 'Stacks - OpenStack Dashboard') def test_resource_types_page_opened(self): resource_types_page = ( self.home_pg.go_to_project_orchestration_resourcetypespage()) self.assertEqual(resource_types_page.page_title, 'Resource Types - OpenStack Dashboard') def test_template_versions_page_opened(self): template_versions_page = ( self.home_pg.go_to_project_orchestration_templateversionspage()) self.assertEqual(template_versions_page.page_title, 'Template Versions - OpenStack Dashboard') def test_template_generator_page_opened(self): template_generator_page = ( self.home_pg.go_to_project_orchestration_templategeneratorpage()) # TODO(e0ne): fix page title once Heat dashaboard will be updated self.assertEqual(template_generator_page.page_title, 'Horizon - OpenStack Dashboard') ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/settings.py0000664000175000017500000000246115257164523021506 0ustar00zuulzuul# # 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. # Default to Horizons test settings to avoid any missing keys import openstack_dashboard.enabled # noqa: F811 from openstack_dashboard.test.settings import * # noqa: F403,H303 from openstack_dashboard.utils import settings import heat_dashboard.enabled # pop these keys to avoid log warnings about deprecation # update_dashboards will populate them anyway HORIZON_CONFIG.pop('dashboards', None) HORIZON_CONFIG.pop('default_dashboard', None) # Update the dashboards with heat_dashboard enabled files # and current INSTALLED_APPS settings.update_dashboards( [ openstack_dashboard.enabled, heat_dashboard.enabled, ], HORIZON_CONFIG, INSTALLED_APPS ) # Remove duplicated apps INSTALLED_APPS = list(set(INSTALLED_APPS)) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765446.4192593 heat_dashboard-16.0.0/heat_dashboard/test/test_data/0000775000175000017500000000000015257164606021243 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/test_data/__init__.py0000664000175000017500000000000015257164523023340 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/test_data/cinder_data.py0000664000175000017500000002063115257164523024052 0ustar00zuulzuul# Copyright 2012 Nebula, 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 cinderclient.v3 import volume_backups as vol_backups from cinderclient.v3 import volume_snapshots as vol_snaps from cinderclient.v3 import volume_types from cinderclient.v3 import volumes from openstack_dashboard import api from openstack_dashboard.test.test_data import utils def data(TEST): TEST.cinder_volumes = utils.TestDataContainer() TEST.cinder_volume_backups = utils.TestDataContainer() TEST.cinder_volume_types = utils.TestDataContainer() TEST.cinder_volume_snapshots = utils.TestDataContainer() # Volumes - Cinder v1 volume = volumes.Volume( volumes.VolumeManager(None), {'id': "11023e92-8008-4c8b-8059-7f2293ff3887", 'status': 'available', 'size': 40, 'display_name': 'Volume name', 'display_description': 'Volume description', 'created_at': '2014-01-27 10:30:00', 'volume_type': None, 'attachments': []}) nameless_volume = volumes.Volume( volumes.VolumeManager(None), {"id": "4b069dd0-6eaa-4272-8abc-5448a68f1cce", "status": 'available', "size": 10, "display_name": '', "display_description": '', "device": "/dev/hda", "created_at": '2010-11-21 18:34:25', "volume_type": 'vol_type_1', "attachments": []}) other_volume = volumes.Volume( volumes.VolumeManager(None), {'id': "21023e92-8008-1234-8059-7f2293ff3889", 'status': 'in-use', 'size': 10, 'display_name': 'my_volume', 'display_description': '', 'created_at': '2013-04-01 10:30:00', 'volume_type': None, 'attachments': [{"id": "1", "server_id": '1', "device": "/dev/hda"}]}) volume_with_type = volumes.Volume( volumes.VolumeManager(None), {'id': "7dcb47fd-07d9-42c2-9647-be5eab799ebe", 'name': 'my_volume2', 'status': 'in-use', 'size': 10, 'display_name': 'my_volume2', 'display_description': '', 'created_at': '2013-04-01 10:30:00', 'volume_type': 'vol_type_2', 'attachments': [{"id": "2", "server_id": '2', "device": "/dev/hdb"}]}) non_bootable_volume = volumes.Volume( volumes.VolumeManager(None), {'id': "21023e92-8008-1234-8059-7f2293ff3890", 'status': 'in-use', 'size': 10, 'display_name': 'my_volume', 'display_description': '', 'created_at': '2013-04-01 10:30:00', 'volume_type': None, 'bootable': False, 'attachments': [{"id": "1", "server_id": '1', "device": "/dev/hda"}]}) volume.bootable = 'true' nameless_volume.bootable = 'true' other_volume.bootable = 'true' TEST.cinder_volumes.add(api.cinder.Volume(volume)) TEST.cinder_volumes.add(api.cinder.Volume(nameless_volume)) TEST.cinder_volumes.add(api.cinder.Volume(other_volume)) TEST.cinder_volumes.add(api.cinder.Volume(volume_with_type)) TEST.cinder_volumes.add(api.cinder.Volume(non_bootable_volume)) vol_type1 = volume_types.VolumeType(volume_types.VolumeTypeManager(None), {'id': '1', 'name': 'vol_type_1', 'description': 'type 1 description', 'extra_specs': {'foo': 'bar', 'volume_backend_name': 'backend_1'}}) vol_type2 = volume_types.VolumeType(volume_types.VolumeTypeManager(None), {'id': '2', 'name': 'vol_type_2', 'description': 'type 2 description'}) vol_type3 = volume_types.VolumeType(volume_types.VolumeTypeManager(None), {'id': '3', 'name': 'vol_type_3', 'is_public': False, 'description': 'type 3 description'}) TEST.cinder_volume_types.add(vol_type1, vol_type2, vol_type3) # Volumes - Cinder v3 (v2 removed in Xena) volume_v3 = volumes.Volume( volumes.VolumeManager(None), {'id': "31023e92-8008-4c8b-8059-7f2293ff1234", 'name': 'v3_volume', 'description': "v3 Volume Description", 'status': 'available', 'size': 20, 'created_at': '2014-01-27 10:30:00', 'volume_type': None, 'os-vol-host-attr:host': 'host@backend-name#pool', 'bootable': 'true', 'attachments': []}) volume_v3.bootable = 'true' TEST.cinder_volumes.add(api.cinder.Volume(volume_v3)) snapshot = vol_snaps.Snapshot( vol_snaps.SnapshotManager(None), {'id': '5f3d1c33-7d00-4511-99df-a2def31f3b5d', 'display_name': 'test snapshot', 'display_description': 'volume snapshot', 'size': 40, 'status': 'available', 'volume_id': '11023e92-8008-4c8b-8059-7f2293ff3887'}) snapshot2 = vol_snaps.Snapshot( vol_snaps.SnapshotManager(None), {'id': 'c9d0881a-4c0b-4158-a212-ad27e11c2b0f', 'name': '', 'description': 'v3 volume snapshot description', 'size': 80, 'status': 'available', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) snapshot3 = vol_snaps.Snapshot( vol_snaps.SnapshotManager(None), {'id': 'c9d0881a-4c0b-4158-a212-ad27e11c2b0e', 'name': '', 'description': 'v3 volume snapshot description 2', 'size': 80, 'status': 'available', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) snapshot4 = vol_snaps.Snapshot( vol_snaps.SnapshotManager(None), {'id': 'cd6be1eb-82ca-4587-8036-13c37c00c2b1', 'name': '', 'description': 'v3 volume snapshot with metadata description', 'size': 80, 'status': 'available', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234', 'metadata': {'snapshot_meta_key': 'snapshot_meta_value'}}) snapshot.bootable = 'true' snapshot2.bootable = 'true' TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot)) TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot2)) TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot3)) TEST.cinder_volume_snapshots.add(api.cinder.VolumeSnapshot(snapshot4)) TEST.cinder_volume_snapshots.first()._volume = volume volume_backup1 = vol_backups.VolumeBackup( vol_backups.VolumeBackupManager(None), {'id': 'a374cbb8-3f99-4c3f-a2ef-3edbec842e31', 'name': 'backup1', 'description': 'volume backup 1', 'size': 10, 'status': 'available', 'container_name': 'volumebackups', 'volume_id': '11023e92-8008-4c8b-8059-7f2293ff3887'}) volume_backup2 = vol_backups.VolumeBackup( vol_backups.VolumeBackupManager(None), {'id': 'c321cbb8-3f99-4c3f-a2ef-3edbec842e52', 'name': 'backup2', 'description': 'volume backup 2', 'size': 20, 'status': 'available', 'container_name': 'volumebackups', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) volume_backup3 = vol_backups.VolumeBackup( vol_backups.VolumeBackupManager(None), {'id': 'c321cbb8-3f99-4c3f-a2ef-3edbec842e53', 'name': 'backup3', 'description': 'volume backup 3', 'size': 20, 'status': 'available', 'container_name': 'volumebackups', 'volume_id': '31023e92-8008-4c8b-8059-7f2293ff1234'}) TEST.cinder_volume_backups.add(volume_backup1) TEST.cinder_volume_backups.add(volume_backup2) TEST.cinder_volume_backups.add(volume_backup3) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/test_data/exceptions.py0000664000175000017500000000325015257164523023774 0ustar00zuulzuul# Copyright 2012 Nebula, 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. import heatclient.exc as heat_exceptions from heat_dashboard.test.test_data import utils def create_stubbed_exception(cls, status_code=500): msg = "Expected failure." def fake_init_exception(self, code=None, message=None, **kwargs): if code is not None: if hasattr(self, 'http_status'): self.http_status = code else: self.code = code self.message = message or self.__class__.message try: # Neutron sometimes updates the message with additional # information, like a reason. self.message = self.message % kwargs except Exception: pass # We still have the main error message. def fake_str(self): return str(self.message) cls.__init__ = fake_init_exception cls.__str__ = fake_str cls.silence_logging = True return cls(status_code, msg) def data(TEST): TEST.exceptions = utils.TestDataContainer() heat_exception = heat_exceptions.HTTPException TEST.exceptions.heat = create_stubbed_exception(heat_exception) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5644958 heat_dashboard-16.0.0/heat_dashboard/test/test_data/glance_data.py0000664000175000017500000003706315257164523024046 0ustar00zuulzuul# Copyright 2012 Nebula, 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 glanceclient.v1 import images from openstack_dashboard import api from heat_dashboard.test.test_data import utils class Namespace(dict): def __repr__(self): return "" % self._info def __init__(self, info): super().__init__() self.__dict__.update(info) self.update(info) self._info = info def as_json(self, indent=4): return self.__dict__ class APIResourceV2(dict): _base_props = [ 'id', 'name', 'status', 'visibility', 'protected', 'checksum', 'owner', 'size', 'virtual_size', 'container_format', 'disk_format', 'created_at', 'updated_at', 'tags', 'direct_url', 'min_ram', 'min_disk', 'self', 'file', 'schema', 'locations'] def __getattr__(self, item): if item == 'schema': return {'properties': {k: '' for k in self._base_props}} else: return self.get(item) def data(TEST): TEST.images = utils.TestDataContainer() TEST.images_api = utils.TestDataContainer() TEST.snapshots = utils.TestDataContainer() TEST.metadata_defs = utils.TestDataContainer() TEST.imagesV2 = utils.TestDataContainer() # Snapshots snapshot_dict = {'name': 'snapshot', 'container_format': 'ami', 'id': 3, 'status': "active", 'owner': TEST.tenant.id, 'properties': {'image_type': 'snapshot'}, 'is_public': False, 'protected': False} snapshot_dict_no_owner = {'name': 'snapshot 2', 'container_format': 'ami', 'id': 4, 'status': "active", 'owner': None, 'properties': {'image_type': 'snapshot'}, 'is_public': False, 'protected': False} snapshot_dict_queued = {'name': 'snapshot 2', 'container_format': 'ami', 'id': 5, 'status': "queued", 'owner': TEST.tenant.id, 'properties': {'image_type': 'snapshot'}, 'is_public': False, 'protected': False} snapshot = images.Image(images.ImageManager(None), snapshot_dict) TEST.snapshots.add(api.glance.Image(snapshot)) snapshot = images.Image(images.ImageManager(None), snapshot_dict_no_owner) TEST.snapshots.add(api.glance.Image(snapshot)) snapshot = images.Image(images.ImageManager(None), snapshot_dict_queued) TEST.snapshots.add(api.glance.Image(snapshot)) # Images image_dict = {'id': '007e7d55-fe1e-4c5c-bf08-44b4a4964822', 'name': 'public_image', 'disk_format': 'qcow2', 'status': "active", 'size': 20 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'novaImage', 'properties': {'image_type': 'image'}, 'is_public': True, 'protected': False, 'min_ram': 0, 'created_at': '2014-02-14T20:56:53'} public_image = images.Image(images.ImageManager(None), image_dict) image_dict = {'id': 'a001c047-22f8-47d0-80a1-8ec94a9524fe', 'name': 'private_image', 'status': "active", 'size': 10 * 1024 ** 2, 'virtual_size': 20 * 1024 ** 2, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'aki', 'is_public': False, 'protected': False, 'min_ram': 0, 'created_at': '2014-03-14T12:56:53'} private_image = images.Image(images.ImageManager(None), image_dict) image_dict = {'id': 'd6936c86-7fec-474a-85c5-5e467b371c3c', 'name': 'protected_images', 'status': "active", 'owner': TEST.tenant.id, 'size': 2 * 1024 ** 3, 'virtual_size': None, 'min_disk': 30, 'container_format': 'novaImage', 'properties': {'image_type': 'image'}, 'is_public': True, 'protected': True, 'min_ram': 0, 'created_at': '2014-03-16T06:22:14'} protected_image = images.Image(images.ImageManager(None), image_dict) image_dict = {'id': '278905a6-4b52-4d1e-98f9-8c57bb25ba32', 'name': None, 'status': "active", 'size': 5 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'novaImage', 'properties': {'image_type': 'image'}, 'is_public': True, 'protected': False, 'min_ram': 0} public_image2 = images.Image(images.ImageManager(None), image_dict) image_dict = {'id': '710a1acf-a3e3-41dd-a32d-5d6b6c86ea10', 'name': 'private_image 2', 'status': "active", 'size': 30 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'aki', 'is_public': False, 'protected': False, 'min_ram': 0} private_image2 = images.Image(images.ImageManager(None), image_dict) image_dict = {'id': '7cd892fd-5652-40f3-a450-547615680132', 'name': 'private_image 3', 'status': "active", 'size': 2 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'aki', 'is_public': False, 'protected': False, 'min_ram': 0} private_image3 = images.Image(images.ImageManager(None), image_dict) # A shared image. Not public and not local tenant. image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849', 'name': 'shared_image 1', 'status': "active", 'size': 8 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': 'someothertenant', 'container_format': 'aki', 'is_public': False, 'protected': False, 'min_ram': 0} shared_image1 = images.Image(images.ImageManager(None), image_dict) # "Official" image. Public and tenant matches an entry # in IMAGES_LIST_FILTER_TENANTS. image_dict = {'id': 'f448704f-0ce5-4d34-8441-11b6581c6619', 'name': 'official_image 1', 'status': "active", 'size': 2 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': 'officialtenant', 'container_format': 'aki', 'is_public': True, 'protected': False, 'min_ram': 0} official_image1 = images.Image(images.ImageManager(None), image_dict) image_dict = {'id': 'a67e7d45-fe1e-4c5c-bf08-44b4a4964822', 'name': 'multi_prop_image', 'status': "active", 'size': 20 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'novaImage', 'properties': {'description': 'a multi prop image', 'foo': 'foo val', 'bar': 'bar val'}, 'is_public': True, 'protected': False} multi_prop_image = images.Image(images.ImageManager(None), image_dict) # An image without name being returned based on current api image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849', 'status': "active", 'size': 8 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': 'someothertenant', 'container_format': 'aki', 'is_public': False, 'protected': False} no_name_image = images.Image(images.ImageManager(None), image_dict) TEST.images_api.add(public_image, private_image, protected_image, public_image2, private_image2, private_image3, shared_image1, official_image1, multi_prop_image) TEST.images.add(api.glance.Image(public_image), api.glance.Image(private_image), api.glance.Image(protected_image), api.glance.Image(public_image2), api.glance.Image(private_image2), api.glance.Image(private_image3), api.glance.Image(shared_image1), api.glance.Image(official_image1), api.glance.Image(multi_prop_image)) TEST.empty_name_image = api.glance.Image(no_name_image) image_v2_dicts = [{ 'checksum': 'eb9139e4942121f22bbc2afc0400b2a4', 'container_format': 'novaImage', 'created_at': '2014-02-14T20:56:53', 'direct_url': 'swift+config://ref1/glance/' 'da8500d5-8b80-4b9c-8410-cc57fb8fb9d5', 'disk_format': 'qcow2', 'file': '/v2/images/' 'da8500d5-8b80-4b9c-8410-cc57fb8fb9d5/file', 'id': '007e7d55-fe1e-4c5c-bf08-44b4a4964822', 'kernel_id': 'f6ebd5f0-b110-4406-8c1e-67b28d4e85e7', 'locations': [ {'metadata': {}, 'url': 'swift+config://ref1/glance/' 'da8500d5-8b80-4b9c-8410-cc57fb8fb9d5'}], 'min_ram': 0, 'name': 'public_image', 'image_type': 'image', 'min_disk': 0, 'owner': TEST.tenant.id, 'protected': False, 'ramdisk_id': '868efefc-4f2d-4ed8-82b1-7e35576a7a47', 'size': 20 * 1024 ** 3, 'status': 'active', 'tags': ['active_image'], 'updated_at': '2015-08-31T19:37:45Z', 'virtual_size': None, 'visibility': 'public' }, { 'checksum': None, 'container_format': 'novaImage', 'created_at': '2014-03-16T06:22:14', 'disk_format': None, 'image_type': 'image', 'file': '/v2/images/885d1cb0-9f5c-4677-9d03-175be7f9f984/file', 'id': 'd6936c86-7fec-474a-85c5-5e467b371c3c', 'locations': [], 'min_disk': 30, 'min_ram': 0, 'name': 'protected_images', 'owner': TEST.tenant.id, 'protected': True, 'size': 2 * 1024 ** 3, 'status': "active", 'tags': ['empty_image'], 'updated_at': '2015-09-01T22:37:32Z', 'virtual_size': None, 'visibility': 'public' }, { 'checksum': 'e533283e6aac072533d1d091a7d2e413', 'container_format': 'novaImage', 'created_at': '2015-09-02T00:31:16Z', 'disk_format': 'qcow2', 'file': '/v2/images/10ca6b6b-48f4-43ac-8159-aa9e9353f5e4/file', 'id': 'a67e7d45-fe1e-4c5c-bf08-44b4a4964822', 'image_type': 'an image type', 'min_disk': 0, 'min_ram': 0, 'name': 'multi_prop_image', 'owner': TEST.tenant.id, 'protected': False, 'size': 20 * 1024 ** 3, 'status': 'active', 'tags': ['custom_property_image'], 'updated_at': '2015-09-02T00:31:17Z', 'virtual_size': None, 'visibility': 'public', 'description': 'a multi prop image', 'foo': 'foo val', 'bar': 'bar val' }] for fixture in image_v2_dicts: apiresource = APIResourceV2(fixture) TEST.imagesV2.add(api.glance.Image(apiresource)) metadef_dict = { 'namespace': 'namespace_1', 'display_name': 'Namespace 1', 'description': 'Mock desc 1', 'resource_type_associations': [ { 'created_at': '2014-08-21T08:39:43Z', 'prefix': 'mock', 'name': 'mock name' } ], 'visibility': 'public', 'protected': True, 'created_at': '2014-08-21T08:39:43Z', 'properties': { 'cpu_mock:mock': { 'default': '1', 'type': 'integer', 'description': 'Number of mocks.', 'title': 'mocks' } } } metadef = Namespace(metadef_dict) TEST.metadata_defs.add(metadef) metadef_dict = { 'namespace': 'namespace_2', 'display_name': 'Namespace 2', 'description': 'Mock desc 2', 'resource_type_associations': [ { 'created_at': '2014-08-21T08:39:43Z', 'prefix': 'mock', 'name': 'mock name' } ], 'visibility': 'private', 'protected': False, 'created_at': '2014-08-21T08:39:43Z', 'properties': { 'hdd_mock:mock': { 'default': '2', 'type': 'integer', 'description': 'Number of mocks.', 'title': 'mocks' } } } metadef = Namespace(metadef_dict) TEST.metadata_defs.add(metadef) metadef_dict = { 'namespace': 'namespace_3', 'display_name': 'Namespace 3', 'description': 'Mock desc 3', 'resource_type_associations': [ { 'created_at': '2014-08-21T08:39:43Z', 'prefix': 'mock', 'name': 'mock name' } ], 'visibility': 'public', 'protected': False, 'created_at': '2014-08-21T08:39:43Z', 'properties': { 'gpu_mock:mock': { 'default': '2', 'type': 'integer', 'description': 'Number of mocks.', 'title': 'mocks' } } } metadef = Namespace(metadef_dict) TEST.metadata_defs.add(metadef) metadef_dict = { 'namespace': 'namespace_4', 'display_name': 'Namespace 4', 'description': 'Mock desc 4', 'resource_type_associations': [ { 'created_at': '2014-08-21T08:39:43Z', 'prefix': 'mock', 'name': 'OS::Cinder::Volume', 'properties_target': 'user' } ], 'visibility': 'public', 'protected': True, 'created_at': '2014-08-21T08:39:43Z', 'properties': { 'ram_mock:mock': { 'default': '2', 'type': 'integer', 'description': 'Number of mocks.', 'title': 'mocks' } } } metadef = Namespace(metadef_dict) TEST.metadata_defs.add(metadef) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.6644952 heat_dashboard-16.0.0/heat_dashboard/test/test_data/heat_data.py0000664000175000017500000004071715257164523023536 0ustar00zuulzuul# 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 heatclient.v1 import resource_types from heatclient.v1 import resources from heatclient.v1 import services from heatclient.v1 import stacks from heatclient.v1 import template_versions from heat_dashboard.test.test_data import utils # A slightly hacked up copy of a sample cloudformation template for testing. TEMPLATE = """ { "AWSTemplateFormatVersion": "2010-09-09", "Description": "AWS CloudFormation Sample Template.", "Parameters": { "KeyName": { "Description": "Name of an EC2 Key Pair to enable SSH access to the instances", "Type": "String" }, "InstanceType": { "Description": "WebServer EC2 instance type", "Type": "String", "Default": "m1.small", "AllowedValues": [ "m1.tiny", "m1.small", "m1.medium", "m1.large", "m1.xlarge" ], "ConstraintDescription": "must be a valid EC2 instance type." }, "DBName": { "Default": "wordpress", "Description": "The WordPress database name", "Type": "String", "MinLength": "1", "MaxLength": "64", "AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", "ConstraintDescription": "must begin with a letter and..." }, "DBUsername": { "Default": "admin", "NoEcho": "true", "Description": "The WordPress database admin account username", "Type": "String", "MinLength": "1", "MaxLength": "16", "AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", "ConstraintDescription": "must begin with a letter and..." }, "DBPassword": { "Default": "admin", "NoEcho": "true", "Description": "The WordPress database admin account password", "Type": "String", "MinLength": "1", "MaxLength": "41", "AllowedPattern": "[a-zA-Z0-9]*", "ConstraintDescription": "must contain only alphanumeric characters." }, "DBRootPassword": { "Default": "admin", "NoEcho": "true", "Description": "Root password for MySQL", "Type": "String", "MinLength": "1", "MaxLength": "41", "AllowedPattern": "[a-zA-Z0-9]*", "ConstraintDescription": "must contain only alphanumeric characters." }, "LinuxDistribution": { "Default": "F17", "Description": "Distribution of choice", "Type": "String", "AllowedValues": [ "F18", "F17", "U10", "RHEL-6.1", "RHEL-6.2", "RHEL-6.3" ] }, "Network": { "Type": "String", "CustomConstraint": "neutron.network" } }, "Mappings": { "AWSInstanceType2Arch": { "m1.tiny": { "Arch": "32" }, "m1.small": { "Arch": "64" }, "m1.medium": { "Arch": "64" }, "m1.large": { "Arch": "64" }, "m1.xlarge": { "Arch": "64" } }, "DistroArch2AMI": { "F18": { "32": "F18-i386-cfntools", "64": "F18-x86_64-cfntools" }, "F17": { "32": "F17-i386-cfntools", "64": "F17-x86_64-cfntools" }, "U10": { "32": "U10-i386-cfntools", "64": "U10-x86_64-cfntools" }, "RHEL-6.1": { "32": "rhel61-i386-cfntools", "64": "rhel61-x86_64-cfntools" }, "RHEL-6.2": { "32": "rhel62-i386-cfntools", "64": "rhel62-x86_64-cfntools" }, "RHEL-6.3": { "32": "rhel63-i386-cfntools", "64": "rhel63-x86_64-cfntools" } } }, "Resources": { "WikiDatabase": { "Type": "AWS::EC2::Instance", "Metadata": { "AWS::CloudFormation::Init": { "config": { "packages": { "yum": { "mysql": [], "mysql-server": [], "httpd": [], "wordpress": [] } }, "services": { "systemd": { "mysqld": { "enabled": "true", "ensureRunning": "true" }, "httpd": { "enabled": "true", "ensureRunning": "true" } } } } } }, "Properties": { "ImageId": { "Fn::FindInMap": [ "DistroArch2AMI", { "Ref": "LinuxDistribution" }, { "Fn::FindInMap": [ "AWSInstanceType2Arch", { "Ref": "InstanceType" }, "Arch" ] } ] }, "InstanceType": { "Ref": "InstanceType" }, "KeyName": { "Ref": "KeyName" }, "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "#!/bin/bash -v\\n", "/opt/aws/bin/cfn-init\\n" ] ] } } } } }, "Outputs": { "WebsiteURL": { "Value": { "Fn::Join": [ "", [ "http://", { "Fn::GetAtt": [ "WikiDatabase", "PublicIp" ] }, "/wordpress" ] ] }, "Description": "URL for Wordpress wiki" } } } """ VALIDATE = """ { "Description": "AWS CloudFormation Sample Template.", "Parameters": { "DBUsername": { "Type": "String", "Description": "The WordPress database admin account username", "Default": "admin", "MinLength": "1", "AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", "NoEcho": "true", "MaxLength": "16", "ConstraintDescription": "must begin with a letter and..." }, "LinuxDistribution": { "Default": "F17", "Type": "String", "Description": "Distribution of choice", "AllowedValues": [ "F18", "F17", "U10", "RHEL-6.1", "RHEL-6.2", "RHEL-6.3" ] }, "DBRootPassword": { "Type": "String", "Description": "Root password for MySQL", "Default": "admin", "MinLength": "1", "AllowedPattern": "[a-zA-Z0-9]*", "NoEcho": "true", "MaxLength": "41", "ConstraintDescription": "must contain only alphanumeric characters." }, "KeyName": { "Type": "String", "Description": "Name of an EC2 Key Pair to enable SSH access to the instances" }, "DBName": { "Type": "String", "Description": "The WordPress database name", "Default": "wordpress", "MinLength": "1", "AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", "MaxLength": "64", "ConstraintDescription": "must begin with a letter and..." }, "DBPassword": { "Type": "String", "Description": "The WordPress database admin account password", "Default": "admin", "MinLength": "1", "AllowedPattern": "[a-zA-Z0-9]*", "NoEcho": "true", "MaxLength": "41", "ConstraintDescription": "must contain only alphanumeric characters." }, "InstanceType": { "Default": "m1.small", "Type": "String", "ConstraintDescription": "must be a valid EC2 instance type.", "Description": "WebServer EC2 instance type", "AllowedValues": [ "m1.tiny", "m1.small", "m1.medium", "m1.large", "m1.xlarge" ] }, "Network": { "Type": "String", "CustomConstraint": "neutron.network" } } } """ ENVIRONMENT = """ parameters: InstanceType: m1.xsmall db_password: verybadpass KeyName: heat_key """ SNAPSHOT_CREATE = """ { "status": "IN_PROGRESS", "name": "None", "data": "None", "creation_time": "2016-02-19T07:25:23.494152", "status_reason": "None", "id": "8af90c07-b788-44ee-a8ab-5990197f5e32" } """ class Environment: def __init__(self, data): self.data = data class Template: def __init__(self, data, validate): self.data = data self.validate = validate class Snapshot: def __init__(self, data): self.data = data def data(TEST): TEST.stacks = utils.TestDataContainer() TEST.stack_templates = utils.TestDataContainer() TEST.stack_environments = utils.TestDataContainer() TEST.stack_snapshot_create = utils.TestDataContainer() TEST.stack_snapshot = utils.TestDataContainer() TEST.resource_types = utils.TestDataContainer() TEST.heat_resources = utils.TestDataContainer() TEST.heat_services = utils.TestDataContainer() TEST.template_versions = utils.TestDataContainer() TEST.template_functions = utils.TestDataContainer() # Services service_1 = services.Service(services.ServiceManager(None), { "status": "up", "binary": "heat-engine", "report_interval": 60, "engine_id": "2f7b5a9b-c50b-4b01-8248-f89f5fb338d1", "created_at": "2015-02-06T03:23:32.000000", "hostname": "mrkanag", "updated_at": "2015-02-20T09:49:52.000000", "topic": "engine", "host": "engine-1", "deleted_at": None, "id": "1efd7015-5016-4caa-b5c8-12438af7b100" }) service_2 = services.Service(services.ServiceManager(None), { "status": "up", "binary": "heat-engine", "report_interval": 60, "engine_id": "2f7b5a9b-c50b-4b01-8248-f89f5fb338d2", "created_at": "2015-02-06T03:23:32.000000", "hostname": "mrkanag", "updated_at": "2015-02-20T09:49:52.000000", "topic": "engine", "host": "engine-2", "deleted_at": None, "id": "1efd7015-5016-4caa-b5c8-12438af7b100" }) TEST.heat_services.add(service_1) TEST.heat_services.add(service_2) # Data return by heatclient. TEST.api_resource_types = utils.TestDataContainer() for i in range(10): stack_data = { "description": "No description", "links": [{ "href": "http://192.168.1.70:8004/v1/" "051c727ee67040d6a7b7812708485a97/" "stacks/stack-test{0}/" "05b4f39f-ea96-4d91-910c-e758c078a089{0}".format(i), "rel": "self" }], "parameters": { 'DBUsername': '******', 'InstanceType': 'm1.small', 'AWS::StackId': ( 'arn:openstack:heat::2ce287:stacks/teststack/88553ec'), 'DBRootPassword': '******', 'AWS::StackName': "teststack{}".format(i), 'DBPassword': '******', 'AWS::Region': 'ap-southeast-1', 'DBName': 'wordpress' }, "stack_status_reason": "Stack successfully created", "stack_name": "stack-test{}".format(i), "creation_time": "2013-04-22T00:11:39Z", "updated_time": "2013-04-22T00:11:39Z", "stack_status": "CREATE_COMPLETE", "id": "05b4f39f-ea96-4d91-910c-e758c078a089{}".format(i) } stack = stacks.Stack(stacks.StackManager(None), stack_data) TEST.stacks.add(stack) for i in range(10): snapshot_data = { "status": "COMPLETE", "name": 'null', "data": { "files": {}, "status": "COMPLETE", "name": "zhao3", "tags": ["a", " 123", " b", " 456"], "stack_user_project_id": "3cba4460875444049a2a7cc5420ccddb", "environment": { "encrypted_param_names": [], "parameter_defaults": {}, "event_sinks": [], "parameters": {}, "resource_registry": { "resources": {} } }, "template": { "heat_template_version": "2013-05-23", "description": "HOT template for Test.", "resources": { "private_subnet": { "type": "OS::Neutron::Subnet", "properties": { "network_id": {"get_resource": "private_net"}, "cidr": "172.16.2.0/24", "gateway_ip": "172.16.2.1" } }, "private_net": { "type": "OS::Neutron::Net", "properties": {"name": "private-net"} } } }, "action": "SNAPSHOT", "project_id": "1acd0026829f4d28bb2eff912d7aad0d", "id": "70650725-bdbd-419f-b53f-5707767bfe0e", "resources": { "private_subnet": { "status": "COMPLETE", "name": "private_subnet", "resource_data": {}, "resource_id": "9c7211b3-31c7-41f6-b92a-442ad3f71ef0", "action": "SNAPSHOT", "type": "OS::Neutron::Subnet", "metadata": {} }, "private_net": { "status": "COMPLETE", "name": "private_net", "resource_data": {}, "resource_id": "ff4fd287-31b2-4d00-bc96-c409bc1db027", "action": "SNAPSHOT", "type": "OS::Neutron::Net", "metadata": {} } } }, "creation_time": "2016-02-21T04:02:54", "status_reason": "Stack SNAPSHOT completed successfully", "id": "01558a3b-ba05-4427-bbb4-1e4ab71cfca{}".format(i) } TEST.stack_snapshot.add(snapshot_data) TEST.stack_templates.add(Template(TEMPLATE, VALIDATE)) TEST.stack_environments.add(Environment(ENVIRONMENT)) TEST.stack_snapshot_create.add(Snapshot(SNAPSHOT_CREATE)) # Resource types list r_type_1 = { "resource_type": "AWS::CloudFormation::Stack", "attributes": {}, "properties": { "Parameters": { "description": "The set of parameters passed to this nested stack.", "immutable": False, "required": False, "type": "map", "update_allowed": True}, "TemplateURL": { "description": "The URL of a template that specifies" " the stack to be created as a resource.", "immutable": False, "required": True, "type": "string", "update_allowed": True}, "TimeoutInMinutes": { "description": "The length of time, in minutes," " to wait for the nested stack creation.", "immutable": False, "required": False, "type": "number", "update_allowed": True} } } r_type_2 = { "resource_type": "OS::Heat::CloudConfig", "attributes": { "config": { "description": "The config value of the software config."} }, "properties": { "cloud_config": { "description": "Map representing the cloud-config data" " structure which will be formatted as YAML.", "immutable": False, "required": False, "type": "map", "update_allowed": False} } } r_types_list = [r_type_1, r_type_2] for rt in r_types_list: r_type = resource_types.ResourceType( resource_types.ResourceTypeManager(None), rt['resource_type']) TEST.resource_types.add(r_type) TEST.api_resource_types.add(rt) # Resources resource_1 = resources.Resource(resources.ResourceManager(None), { "logical_resource_id": "my_resource", "physical_resource_id": "7b5e29b1-c94d-402d-b69c-df9ac6dfc0ce", "resource_name": "my_resource", "links": [ { "href": "http://192.168.1.70:8004/v1/" "051c727ee67040d6a7b7812708485a97/" "stacks/%s/%s/resources/my_resource" % (TEST.stacks.first().stack_name, TEST.stacks.first().id), "rel": "self" }, { "href": "http://192.168.1.70:8004/v1/" "051c727ee67040d6a7b7812708485a97/" "stacks/%s/%s" % (TEST.stacks.first().stack_name, TEST.stacks.first().id), "rel": "stack" } ], "attributes": { "metadata": {} } }) TEST.heat_resources.add(resource_1) # Template versions template_version_1 = template_versions.TemplateVersion( template_versions.TemplateVersionManager(None), { "version": "HeatTemplateFormatVersion.2012-12-12", "type": "cfn" }) template_version_2 = template_versions.TemplateVersion( template_versions.TemplateVersionManager(None), { "version": "heat_template_version.2013-05-23", "type": "hot" }) TEST.template_versions.add(template_version_1) TEST.template_versions.add(template_version_2) # Template functions template_function_1 = template_versions.TemplateVersion( template_versions.TemplateVersionManager(None), { "functions": "Fn::GetAZs", "description": "A function for retrieving the availability zones." }) template_function_2 = template_versions.TemplateVersion( template_versions.TemplateVersionManager(None), { "functions": "Fn::Join", "description": "A function for joining strings." }) TEST.template_functions.add(template_function_1) TEST.template_functions.add(template_function_2) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5664957 heat_dashboard-16.0.0/heat_dashboard/test/test_data/keystone_data.py0000664000175000017500000003142415257164523024451 0ustar00zuulzuul# Copyright 2012 Nebula, 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. import copy import datetime from django.conf import settings from keystoneclient import access from keystoneclient.v2_0 import tenants from keystoneclient.v2_0 import users from keystoneclient.v3.contrib.federation import identity_providers from keystoneclient.v3.contrib.federation import mappings from keystoneclient.v3.contrib.federation import protocols from keystoneclient.v3 import domains from openstack_auth import user as auth_user from heat_dashboard.test.test_data import utils # Dummy service catalog with all service. # All endpoint URLs should point to example.com. # Try to keep them as accurate to real data as possible (ports, URIs, etc.) SERVICE_CATALOG = [ {"type": "compute", "name": "nova", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.nova.example.com:8774/v2", "internalURL": "http://int.nova.example.com:8774/v2", "publicURL": "http://public.nova.example.com:8774/v2"}, {"region": "RegionTwo", "adminURL": "http://admin.nova2.example.com:8774/v2", "internalURL": "http://int.nova2.example.com:8774/v2", "publicURL": "http://public.nova2.example.com:8774/v2"}]}, {"type": "volumev2", "name": "cinderv2", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.nova.example.com:8776/v2", "internalURL": "http://int.nova.example.com:8776/v2", "publicURL": "http://public.nova.example.com:8776/v2"}, {"region": "RegionTwo", "adminURL": "http://admin.nova.example.com:8776/v2", "internalURL": "http://int.nova.example.com:8776/v2", "publicURL": "http://public.nova.example.com:8776/v2"}]}, {"type": "image", "name": "glance", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.glance.example.com:9292", "internalURL": "http://int.glance.example.com:9292", "publicURL": "http://public.glance.example.com:9292"}]}, {"type": "identity", "name": "keystone", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.keystone.example.com:35357/v2.0", "internalURL": "http://int.keystone.example.com:5000/v2.0", "publicURL": "http://public.keystone.example.com:5000/v2.0"}]}, {"type": "object-store", "name": "swift", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.swift.example.com:8080/", "internalURL": "http://int.swift.example.com:8080/", "publicURL": "http://public.swift.example.com:8080/"}]}, {"type": "network", "name": "neutron", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.neutron.example.com:9696/", "internalURL": "http://int.neutron.example.com:9696/", "publicURL": "http://public.neutron.example.com:9696/"}]}, {"type": "ec2", "name": "EC2 Service", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.nova.example.com:8773/services/Admin", "publicURL": "http://public.nova.example.com:8773/services/Cloud", "internalURL": "http://int.nova.example.com:8773/services/Cloud"}]}, {"type": "orchestration", "name": "Heat", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.heat.example.com:8004/v1", "publicURL": "http://public.heat.example.com:8004/v1", "internalURL": "http://int.heat.example.com:8004/v1"}]} ] def data(TEST): # Make a deep copy of the catalog to avoid persisting side-effects # when tests modify the catalog. TEST.service_catalog = copy.deepcopy(SERVICE_CATALOG) TEST.tokens = utils.TestDataContainer() TEST.domains = utils.TestDataContainer() TEST.users = utils.TestDataContainer() # TEST.groups = utils.TestDataContainer() TEST.tenants = utils.TestDataContainer() # TEST.role_assignments = utils.TestDataContainer() # TEST.roles = utils.TestDataContainer() # TEST.ec2 = utils.TestDataContainer() TEST.identity_providers = utils.TestDataContainer() TEST.idp_mappings = utils.TestDataContainer() TEST.idp_protocols = utils.TestDataContainer() # admin_role_dict = {'id': '1', # 'name': 'admin'} # admin_role = roles.Role(roles.RoleManager, admin_role_dict, loaded=True) member_role_dict = {'id': "2", 'name': settings.OPENSTACK_KEYSTONE_DEFAULT_ROLE} # member_role = roles.Role(roles.RoleManager, # member_role_dict, loaded=True) # TEST.roles.add(admin_role, member_role) # TEST.roles.admin = admin_role # TEST.roles.member = member_role domain_dict = {'id': "1", 'name': 'test_domain', 'description': "a test domain.", 'enabled': True} domain_dict_2 = {'id': "2", 'name': 'disabled_domain', 'description': "a disabled test domain.", 'enabled': False} domain_dict_3 = {'id': "3", 'name': 'another_test_domain', 'description': "another test domain.", 'enabled': True} domain = domains.Domain(domains.DomainManager, domain_dict) disabled_domain = domains.Domain(domains.DomainManager, domain_dict_2) another_domain = domains.Domain(domains.DomainManager, domain_dict_3) TEST.domains.add(domain, disabled_domain, another_domain) TEST.domain = domain # Your "current" domain user_dict = {'id': "1", 'name': 'test_user', 'description': 'test_description', 'email': 'test@example.com', 'password': 'password', 'token': 'test_token', 'project_id': '1', 'enabled': True, 'domain_id': "1"} user = users.User(None, user_dict) user_dict = {'id': "2", 'name': 'user_two', 'description': 'test_description', 'email': 'two@example.com', 'password': 'password', 'token': 'test_token', 'project_id': '1', 'enabled': True, 'domain_id': "1"} user2 = users.User(None, user_dict) user_dict = {'id': "3", 'name': 'user_three', 'description': 'test_description', 'email': 'three@example.com', 'password': 'password', 'token': 'test_token', 'project_id': '1', 'enabled': True, 'domain_id': "1"} user3 = users.User(None, user_dict) user_dict = {'id': "4", 'name': 'user_four', 'description': 'test_description', 'email': 'four@example.com', 'password': 'password', 'token': 'test_token', 'project_id': '2', 'enabled': True, 'domain_id': "2"} user4 = users.User(None, user_dict) user_dict = {'id': "5", 'name': 'user_five', 'description': 'test_description', 'email': None, 'password': 'password', 'token': 'test_token', 'project_id': '2', 'enabled': True, 'domain_id': "1"} user5 = users.User(None, user_dict) TEST.users.add(user, user2, user3, user4, user5) TEST.user = user # Your "current" user TEST.user.service_catalog = copy.deepcopy(SERVICE_CATALOG) tenant_dict = {'id': "1", 'name': 'test_tenant', 'description': "a test tenant.", 'enabled': True, 'domain_id': '1', 'domain_name': 'test_domain'} tenant_dict_2 = {'id': "2", 'name': 'disabled_tenant', 'description': "a disabled test tenant.", 'enabled': False, 'domain_id': '2', 'domain_name': 'disabled_domain'} tenant_dict_3 = {'id': "3", 'name': '\u4e91\u89c4\u5219', 'description': "an unicode-named tenant.", 'enabled': True, 'domain_id': '2', 'domain_name': 'disabled_domain'} tenant = tenants.Tenant(tenants.TenantManager, tenant_dict) disabled_tenant = tenants.Tenant(tenants.TenantManager, tenant_dict_2) tenant_unicode = tenants.Tenant(tenants.TenantManager, tenant_dict_3) TEST.tenants.add(tenant, disabled_tenant, tenant_unicode) TEST.tenant = tenant # Your "current" tenant tomorrow = datetime.datetime.now() + datetime.timedelta(days=1) expiration = tomorrow.isoformat() scoped_token_dict = { 'access': { 'token': { 'id': "test_token_id", 'expires': expiration, 'tenant': tenant_dict, 'tenants': [tenant_dict]}, 'user': { 'id': "test_user_id", 'name': "test_user", 'roles': [member_role_dict]}, 'serviceCatalog': TEST.service_catalog } } scoped_access_info = access.AccessInfo.factory(resp=None, body=scoped_token_dict) unscoped_token_dict = { 'access': { 'token': { 'id': "test_token_id", 'expires': expiration}, 'user': { 'id': "test_user_id", 'name': "test_user", 'roles': [member_role_dict]}, 'serviceCatalog': TEST.service_catalog } } unscoped_access_info = access.AccessInfo.factory(resp=None, body=unscoped_token_dict) scoped_token = auth_user.Token(scoped_access_info) unscoped_token = auth_user.Token(unscoped_access_info) TEST.tokens.add(scoped_token, unscoped_token) TEST.token = scoped_token # your "current" token. TEST.tokens.scoped_token = scoped_token TEST.tokens.unscoped_token = unscoped_token idp_dict_1 = {'id': 'idp_1', 'description': 'identity provider 1', 'enabled': True, 'remote_ids': ['rid_1', 'rid_2']} idp_1 = identity_providers.IdentityProvider( identity_providers.IdentityProviderManager, idp_dict_1, loaded=True) idp_dict_2 = {'id': 'idp_2', 'description': 'identity provider 2', 'enabled': True, 'remote_ids': ['rid_3', 'rid_4']} idp_2 = identity_providers.IdentityProvider( identity_providers.IdentityProviderManager, idp_dict_2, loaded=True) TEST.identity_providers.add(idp_1, idp_2) idp_mapping_dict = { "id": "mapping_1", "rules": [ { "local": [ { "user": { "name": "{0}" } }, { "group": { "id": "0cd5e9" } } ], "remote": [ { "type": "UserName" }, { "type": "orgPersonType", "not_any_of": [ "Contractor", "Guest" ] } ] } ] } idp_mapping = mappings.Mapping( mappings.MappingManager(None), idp_mapping_dict) TEST.idp_mappings.add(idp_mapping) idp_protocol_dict_1 = {'id': 'protocol_1', 'mapping_id': 'mapping_1'} idp_protocol = protocols.Protocol( protocols.ProtocolManager, idp_protocol_dict_1, loaded=True) TEST.idp_protocols.add(idp_protocol) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5664957 heat_dashboard-16.0.0/heat_dashboard/test/test_data/neutron_data.py0000664000175000017500000002065615257164523024307 0ustar00zuulzuul# Copyright 2012 Nebula, 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. import copy from openstack.network.v2 import network as sdk_net from openstack.network.v2 import subnet as sdk_subnet from openstack_dashboard.api import neutron from heat_dashboard.test.test_data import utils def data(TEST): # Data returned by openstack_dashboard.api.neutron wrapper. TEST.networks = utils.TestDataContainer() TEST.subnets = utils.TestDataContainer() TEST.ports = utils.TestDataContainer() TEST.routers = utils.TestDataContainer() TEST.floating_ips = utils.TestDataContainer() TEST.security_groups = utils.TestDataContainer() TEST.qos_policies = utils.TestDataContainer() # Data returned by SDK: TEST.api_networks = list() TEST.api_subnets = list() # 1st network. network_dict = {'is_admin_state_up': True, 'id': '82288d84-e0a5-42ac-95be-e6af08727e42', 'name': 'net1', 'status': 'ACTIVE', 'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9', '41e53a49-442b-4307-9e9a-88967a6b6657'], 'subnet_ids': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9', '41e53a49-442b-4307-9e9a-88967a6b6657'], 'tenant_id': '1', 'is_router_external': False, 'router:external': False, 'is_shared': False, 'shared': False} subnet_dict = {'allocation_pools': [{'end': '10.0.0.254', 'start': '10.0.0.2'}], 'dns_nameservers': [], 'host_routes': [], 'cidr': '10.0.0.0/24', 'is_dhcp_enabled': True, 'gateway_ip': '10.0.0.1', 'id': network_dict['subnet_ids'][0], 'ip_version': 4, 'name': 'mysubnet1', 'network_id': network_dict['id'], 'tenant_id': network_dict['tenant_id']} subnetv6_dict = { 'allocation_pools': [{'start': 'fdb6:b88a:488e::2', 'end': 'fdb6:b88a:488e:0:ffff:ffff:ffff:ffff'}], 'dns_nameservers': [], 'host_routes': [], 'cidr': 'fdb6:b88a:488e::/64', 'is_dhcp_enabled': True, 'gateway_ip': 'fdb6:b88a:488e::1', 'id': network_dict['subnet_ids'][1], 'ip_version': 6, 'name': 'myv6subnet', 'network_id': network_dict['id'], 'tenant_id': network_dict['tenant_id'], 'ipv6_ra_mode': 'slaac', 'ipv6_address_mode': 'slaac' } TEST.api_networks.append(sdk_net.Network(**network_dict)) TEST.api_subnets.append(sdk_subnet.Subnet(**subnet_dict)) TEST.api_subnets.append(sdk_subnet.Subnet(**subnetv6_dict)) network = copy.deepcopy(network_dict) subnet = neutron.Subnet(subnet_dict) subnetv6 = neutron.Subnet(subnetv6_dict) network['subnets'] = [subnet, subnetv6] TEST.networks.add(neutron.Network(network)) TEST.subnets.add(subnet) TEST.subnets.add(subnetv6) # Ports on 1st network. port_dict = { 'admin_state_up': True, 'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890', 'device_owner': 'network:dhcp', 'fixed_ips': [{'ip_address': '10.0.0.3', 'subnet_id': subnet_dict['id']}], 'id': '063cf7f3-ded1-4297-bc4c-31eae876cc91', 'mac_address': 'fa:16:3e:9c:d5:7e', 'name': '', 'network_id': network_dict['id'], 'status': 'ACTIVE', 'tenant_id': network_dict['tenant_id'], 'binding:vnic_type': 'normal', 'binding:host_id': 'host', 'allowed_address_pairs': [ {'ip_address': '174.0.0.201', 'mac_address': 'fa:16:3e:7a:7b:18'} ], 'port_security_enabled': True, 'security_groups': [], } TEST.ports.add(neutron.Port(port_dict)) # External network. network_dict = {'is_admin_state_up': True, 'id': '9b466b94-213a-4cda-badf-72c102a874da', 'name': 'ext_net', 'status': 'ACTIVE', 'subnets': ['d6bdc71c-7566-4d32-b3ff-36441ce746e8'], 'subnet_ids': ['d6bdc71c-7566-4d32-b3ff-36441ce746e8'], 'tenant_id': '3', 'is_router_external': True, 'router:external': True, 'is_shared': False, 'shared': False} subnet_dict = {'allocation_pools': [{'start': '172.24.4.226.', 'end': '172.24.4.238'}], 'dns_nameservers': [], 'host_routes': [], 'cidr': '172.24.4.0/28', 'is_dhcp_enabledcp': False, 'gateway_ip': '172.24.4.225', 'id': 'd6bdc71c-7566-4d32-b3ff-36441ce746e8', 'ip_version': 4, 'name': 'ext_subnet', 'network_id': network_dict['id'], 'tenant_id': network_dict['tenant_id']} TEST.api_networks.append(sdk_net.Network(**network_dict)) TEST.api_subnets.append(sdk_subnet.Subnet(**subnet_dict)) TEST.api_subnets.append(sdk_subnet.Subnet(**subnetv6_dict)) ext_net = network_dict network = copy.deepcopy(network_dict) subnet = neutron.Subnet(subnet_dict) network['subnets'] = [subnet] TEST.networks.add(neutron.Network(network)) TEST.subnets.add(subnet) assoc_port = port_dict router_dict = {'id': '279989f7-54bb-41d9-ba42-0d61f12fda61', 'name': 'router1', 'status': 'ACTIVE', 'is_admin_state_up': True, 'distributed': True, 'external_gateway_info': {'network_id': ext_net['id']}, 'tenant_id': '1', 'availability_zone_hints': ['nova']} TEST.routers.add(neutron.Router(router_dict)) # Associated (with compute port on 1st network). fip_dict = {'tenant_id': '1', 'floating_ip_address': '172.16.88.228', 'floating_network_id': ext_net['id'], 'id': 'a97af8f2-3149-4b97-abbd-e49ad19510f7', 'fixed_ip_address': assoc_port['fixed_ips'][0]['ip_address'], 'port_id': assoc_port['id'], 'router_id': router_dict['id']} fip_with_instance = copy.deepcopy(fip_dict) fip_with_instance.update({'instance_id': '1', 'instance_type': 'compute'}) TEST.floating_ips.add(neutron.FloatingIp(fip_with_instance)) # Security group. sec_group_1 = {'tenant_id': '1', 'description': 'default', 'id': 'faad7c80-3b62-4440-967c-13808c37131d', 'name': 'default'} sec_group_2 = {'tenant_id': '1', 'description': 'NotDefault', 'id': '27a5c9a1-bdbb-48ac-833a-2e4b5f54b31d', 'name': 'other_group'} sec_group_3 = {'tenant_id': '1', 'description': 'NotDefault', 'id': '443a4d7a-4bd2-4474-9a77-02b35c9f8c95', 'name': 'another_group'} groups = [sec_group_1, sec_group_2, sec_group_3] sg_name_dict = {sg['id']: sg['name'] for sg in groups} for sg in groups: sg['security_group_rules'] = [] # OpenStack Dashboard internaly API. TEST.security_groups.add( neutron.SecurityGroup(copy.deepcopy(sg), sg_name_dict)) # qos policies policy_dict = {'id': 'a21dcd22-7189-cccc-aa32-22adafaf16a7', 'name': 'policy 1', 'tenant_id': '1'} TEST.qos_policies.add(neutron.QoSPolicy(policy_dict)) policy_dict1 = {'id': 'a21dcd22-7189-ssss-aa32-22adafaf16a7', 'name': 'policy 2', 'tenant_id': '1'} TEST.qos_policies.add(neutron.QoSPolicy(policy_dict1)) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1790765394.5674958 heat_dashboard-16.0.0/heat_dashboard/test/test_data/nova_data.py0000664000175000017500000001643115257164523023554 0ustar00zuulzuul# Copyright 2012 Nebula, 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. import json from novaclient.v2 import flavors from novaclient.v2 import keypairs from novaclient.v2 import servers from openstack.compute.v2 import availability_zone as az_resource from openstack.test import fakes from heat_dashboard.test.test_data import utils class FlavorExtraSpecs(dict): def __repr__(self): return "" % self._info def __init__(self, info): super().__init__() self.__dict__.update(info) self.update(info) self._info = info SERVER_DATA = """ { "server": { "OS-EXT-SRV-ATTR:instance_name": "instance-00000005", "OS-EXT-SRV-ATTR:host": "instance-host", "OS-EXT-STS:task_state": null, "addresses": { "private": [ { "version": 4, "addr": "10.0.0.1" } ] }, "links": [ { "href": "%(host)s/v1.1/%(tenant_id)s/servers/%(server_id)s", "rel": "self" }, { "href": "%(host)s/%(tenant_id)s/servers/%(server_id)s", "rel": "bookmark" } ], "image": { "id": "%(image_id)s", "links": [ { "href": "%(host)s/%(tenant_id)s/images/%(image_id)s", "rel": "bookmark" } ] }, "OS-EXT-STS:vm_state": "active", "flavor": { "id": "%(flavor_id)s", "links": [ { "href": "%(host)s/%(tenant_id)s/flavors/%(flavor_id)s", "rel": "bookmark" } ] }, "id": "%(server_id)s", "user_id": "%(user_id)s", "OS-DCF:diskConfig": "MANUAL", "accessIPv4": "", "accessIPv6": "", "progress": null, "OS-EXT-STS:power_state": 1, "config_drive": "", "status": "%(status)s", "updated": "2012-02-28T19:51:27Z", "hostId": "c461ea283faa0ab5d777073c93b126c68139e4e45934d4fc37e403c2", "key_name": "%(key_name)s", "name": "%(name)s", "created": "2012-02-28T19:51:17Z", "tenant_id": "%(tenant_id)s", "metadata": {"someMetaLabel": "someMetaData", "somehtmllabel": "