././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6210837 heat-dashboard-2.1.0.dev23/0000755000175000017500000000000000000000000017215 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/.eslintignore0000644000175000017500000000012400000000000021715 0ustar00jamespagejamespage00000000000000heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/libs/* ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/.eslintrc.js0000644000175000017500000002036200000000000021457 0ustar00jamespagejamespage00000000000000module.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" } }; ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/.zuul.yaml0000644000175000017500000000177600000000000021171 0ustar00jamespagejamespage00000000000000- 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: - ^.*\.rst$ - ^doc/.*$ - ^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-nodejs10-jobs - openstack-lower-constraints-jobs - openstack-python3-ussuri-jobs-horizon - publish-openstack-docs-pti - release-notes-jobs-python3 check: jobs: - heat-dashboard-integration-tests: voting: false ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236224.0 heat-dashboard-2.1.0.dev23/AUTHORS0000644000175000017500000001547000000000000020274 0ustar00jamespagejamespage00000000000000Aaron 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 CristianFiorentino 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 Gloria Gu Gábor Antal He Yongli Itxaka 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 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 Peter Belanyi 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 Sergei Chipiga Sergey Lukjanov Shane Wang Steve Baker Steve Leon Steve McLellan Sushil Kumar Tatiana Mazur Tatiana Ovchinnikova Thai Tran Thomas Bechtold Thomas Goirand Tim Schnell Timur Sufiev Travis Tripp Trinh Nguyen Tyr Johanson Vic Howard Victor Stinner Vlad Okhrimenko Vu Cong Tuan Wu Wenxiang Xinni Ge Ying Zuo 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 pengyuesheng ricolin ricolin root shizhihui simon tianliang wangbo wangqi wei.ying wu.chunyang xurong00037997 yaraat zhang.lei zhangboye zhaozhilong zhu.rong zhurong ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/CONTRIBUTING.rst0000644000175000017500000000125100000000000021655 0ustar00jamespagejamespage00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: https://docs.openstack.org/infra/manual/developers.html If you already have a good understanding of how the system works and your OpenStack accounts are set up, you can skip to the development workflow section of this documentation to learn how changes to OpenStack should be submitted for review via the Gerrit tool: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on StoryBoard, not GitHub and Launchpad: https://storyboard.openstack.org/#!/project/992 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236224.0 heat-dashboard-2.1.0.dev23/ChangeLog0000644000175000017500000006511300000000000020775 0ustar00jamespagejamespage00000000000000CHANGES ======= * 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 * 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/HACKING.rst0000644000175000017500000000023700000000000021015 0ustar00jamespagejamespage00000000000000openstack Style Commandments =============================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/LICENSE0000644000175000017500000002363700000000000020235 0ustar00jamespagejamespage00000000000000 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/MANIFEST.in0000644000175000017500000000016600000000000020756 0ustar00jamespagejamespage00000000000000recursive-include heat_dashboard *.html *.scss *.css *.js *.map *.svg *.png *.json include AUTHORS include ChangeLog ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6210837 heat-dashboard-2.1.0.dev23/PKG-INFO0000644000175000017500000000276100000000000020320 0ustar00jamespagejamespage00000000000000Metadata-Version: 1.2 Name: heat-dashboard Version: 2.1.0.dev23 Summary: Heat Management Dashboard Home-page: https://docs.openstack.org/heat-dashboard/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ========================== Welcome to Heat Dashboard! ========================== 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 Team and repository tags ------------------------ .. image:: https://governance.openstack.org/tc/badges/heat-dashboard.svg :target: https://governance.openstack.org/tc/reference/tags/index.html Platform: UNKNOWN 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 :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/README.rst0000644000175000017500000000116100000000000020703 0ustar00jamespagejamespage00000000000000========================== Welcome to Heat Dashboard! ========================== 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 Team and repository tags ------------------------ .. image:: https://governance.openstack.org/tc/badges/heat-dashboard.svg :target: https://governance.openstack.org/tc/reference/tags/index.html ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236225.0 heat-dashboard-2.1.0.dev23/RELEASENOTES.rst0000644000175000017500000000072100000000000021640 0ustar00jamespagejamespage00000000000000============== heat-dashboard ============== .. _heat-dashboard_2.0.0-14: 2.0.0-14 ======== .. _heat-dashboard_2.0.0-14_Upgrade Notes: Upgrade Notes ------------- .. releasenotes/notes/drop-py-2-7-3dafc6e9e6f29bda.yaml @ b'8638a0c7f6709c2f17903d17aedb462daa65b7e9' - Python 2.7 support has been dropped. Last release of heat-dashboard to support Python 2.7 is OpenStack Train. The minimum version of Python now supported by heat-dashboard is Python 3.6. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/babel-django.cfg0000644000175000017500000000011500000000000022200 0ustar00jamespagejamespage00000000000000[python: **.py] [django: **/templates/**.html] [django: **/templates/**.csv] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/babel-djangojs.cfg0000644000175000017500000000006100000000000022535 0ustar00jamespagejamespage00000000000000[javascript: **.js] [angular: **/static/**.html] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/bindep.txt0000644000175000017500000000024000000000000021213 0ustar00jamespagejamespage00000000000000# selenium tests firefox [selenium] xvfb [selenium platform:dpkg] # already part of xorg-x11-server on openSUSE xorg-x11-server-Xvfb [selenium platform:redhat] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5570838 heat-dashboard-2.1.0.dev23/devstack/0000755000175000017500000000000000000000000021021 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/devstack/plugin.sh0000644000175000017500000000446400000000000022663 0ustar00jamespagejamespage00000000000000# plugin.sh - DevStack plugin.sh dispatch script heat-dashboard HEAT_DASHBOARD_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd) function install_heat_dashboard { # NOTE(shu-mutou): workaround for devstack bug: 1540328 # where devstack install 'test-requirements' but should not do it # for heat-dashboard project as it installs Horizon from url. # Remove following two 'mv' commands when mentioned bug is fixed. mv $HEAT_DASHBOARD_DIR/test-requirements.txt $HEAT_DASHBOARD_DIR/_test-requirements.txt setup_develop ${HEAT_DASHBOARD_DIR} mv $HEAT_DASHBOARD_DIR/_test-requirements.txt $HEAT_DASHBOARD_DIR/test-requirements.txt } 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.json ${DEST}/horizon/openstack_dashboard/conf/ # 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/devstack/settings0000644000175000017500000000011000000000000022574 0ustar00jamespagejamespage00000000000000# settings file for heat-dashboard plugin enable_service heat-dashboard ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/doc/0000755000175000017500000000000000000000000017762 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/requirements.txt0000644000175000017500000000064100000000000023247 0ustar00jamespagejamespage00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. openstackdocstheme>=1.18.1 # Apache-2.0 sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD reno>=2.5.0 # Apache-2.0 sphinxcontrib-httpdomain>=1.3.0 # BSD sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/doc/source/0000755000175000017500000000000000000000000021262 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/conf.py0000755000175000017500000000613000000000000022564 0ustar00jamespagejamespage00000000000000# -*- coding: utf-8 -*- # 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 = u'Heat Dashboard' copyright = u'2017, OpenStack Developers' # openstackdocstheme options repository_name = 'openstack/heat-dashboard' bug_project = '992' bug_tag = 'doc' html_last_updated_fmt = '%Y-%m-%d %H:%M' # 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 = 'sphinx' # -- 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', u'Heat Dashboard Documentation', u'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', u'Heat Dashboard Documentation', 'Documentation for Heat Dashboard plugin to Openstack\ Dashboard (Horizon)', [u'OpenStack'], 1) ] # Example configuration for intersphinx: refer to the Python standard library. # intersphinx_mapping = {'http://docs.python.org/': None} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/doc/source/configuration/0000755000175000017500000000000000000000000024131 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/configuration/configuration.rst0000644000175000017500000000147700000000000027543 0ustar00jamespagejamespage00000000000000============= 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". ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/configuration/index.rst0000644000175000017500000000023100000000000025766 0ustar00jamespagejamespage00000000000000================================== Heat Dashboard configuration guide ================================== .. toctree:: :maxdepth: 1 configuration ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/doc/source/contributor/0000755000175000017500000000000000000000000023634 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/contributor/contributing.rst0000644000175000017500000000122700000000000027077 0ustar00jamespagejamespage00000000000000================= How to Contribute ================= Contributor License Agreement ----------------------------- .. index:: single: license; agreement In order to contribute to the Heat Dashboard project, you need to have signed OpenStack's contributor's agreement. .. seealso:: * https://docs.openstack.org/infra/manual/developers.html * https://wiki.openstack.org/CLA Project Hosting Details ------------------------- Bug tracker https://storyboard.openstack.org/#!/project/992 Code Hosting https://opendev.org/openstack/heat-dashboard/ Code Review https://review.opendev.org/#/q/status:open+project:openstack/heat-dashboard,n,z ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/contributor/devstack.rst0000644000175000017500000000052400000000000026173 0ustar00jamespagejamespage00000000000000============================== 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/contributor/index.rst0000644000175000017500000000021600000000000025474 0ustar00jamespagejamespage00000000000000=========================== Contributor Documentation =========================== .. toctree:: :maxdepth: 2 contributing devstack ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/index.rst0000644000175000017500000000074000000000000023124 0ustar00jamespagejamespage00000000000000.. 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/doc/source/install/0000755000175000017500000000000000000000000022730 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/install/index.rst0000644000175000017500000000516500000000000024600 0ustar00jamespagejamespage00000000000000================================= 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.json' .. 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 service apache2 restart ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/doc/source/user/0000755000175000017500000000000000000000000022240 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/user/index.rst0000644000175000017500000000025500000000000024103 0ustar00jamespagejamespage00000000000000================================= Heat Dashboard User Documentation ================================= .. toctree:: :maxdepth: 2 stacks.rst template_generator.rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/user/stacks.rst0000644000175000017500000001616000000000000024266 0ustar00jamespagejamespage00000000000000======================== 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/doc/source/user/template_generator.rst0000644000175000017500000000353300000000000026657 0ustar00jamespagejamespage00000000000000====================================== 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/0000755000175000017500000000000000000000000022145 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/__init__.py0000644000175000017500000000000000000000000024244 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/api/0000755000175000017500000000000000000000000022716 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/api/__init__.py0000644000175000017500000000300400000000000025024 0ustar00jamespagejamespage00000000000000# 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", ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/api/heat.py0000644000175000017500000002135100000000000024213 0ustar00jamespagejamespage00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/api/rest/0000755000175000017500000000000000000000000023673 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/api/rest/__init__.py0000644000175000017500000000176400000000000026014 0ustar00jamespagejamespage00000000000000# 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', ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/api/rest/heat.py0000644000175000017500000000333000000000000025165 0ustar00jamespagejamespage00000000000000# 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, '') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/conf/0000755000175000017500000000000000000000000023072 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/conf/heat_policy.json0000644000175000017500000001070000000000000026263 0ustar00jamespagejamespage00000000000000{ "context_is_admin": "role:admin", "deny_stack_user": "not role:heat_stack_user", "deny_everybody": "!", "cloudformation:ListStacks": "rule:deny_stack_user", "cloudformation:CreateStack": "rule:deny_stack_user", "cloudformation:DescribeStacks": "rule:deny_stack_user", "cloudformation:DeleteStack": "rule:deny_stack_user", "cloudformation:UpdateStack": "rule:deny_stack_user", "cloudformation:CancelUpdateStack": "rule:deny_stack_user", "cloudformation:DescribeStackEvents": "rule:deny_stack_user", "cloudformation:ValidateTemplate": "rule:deny_stack_user", "cloudformation:GetTemplate": "rule:deny_stack_user", "cloudformation:EstimateTemplateCost": "rule:deny_stack_user", "cloudformation:DescribeStackResource": "", "cloudformation:DescribeStackResources": "rule:deny_stack_user", "cloudformation:ListStackResources": "rule:deny_stack_user", "cloudwatch:DeleteAlarms": "rule:deny_stack_user", "cloudwatch:DescribeAlarmHistory": "rule:deny_stack_user", "cloudwatch:DescribeAlarms": "rule:deny_stack_user", "cloudwatch:DescribeAlarmsForMetric": "rule:deny_stack_user", "cloudwatch:DisableAlarmActions": "rule:deny_stack_user", "cloudwatch:EnableAlarmActions": "rule:deny_stack_user", "cloudwatch:GetMetricStatistics": "rule:deny_stack_user", "cloudwatch:ListMetrics": "rule:deny_stack_user", "cloudwatch:PutMetricAlarm": "rule:deny_stack_user", "cloudwatch:PutMetricData": "", "cloudwatch:SetAlarmState": "rule:deny_stack_user", "actions:action": "rule:deny_stack_user", "build_info:build_info": "rule:deny_stack_user", "events:index": "rule:deny_stack_user", "events:show": "rule:deny_stack_user", "resource:index": "rule:deny_stack_user", "resource:metadata": "", "resource:signal": "", "resource:mark_unhealthy": "rule:deny_stack_user", "resource:show": "rule:deny_stack_user", "stacks:abandon": "rule:deny_stack_user", "stacks:create": "rule:deny_stack_user", "stacks:delete": "rule:deny_stack_user", "stacks:detail": "rule:deny_stack_user", "stacks:export": "rule:deny_stack_user", "stacks:generate_template": "rule:deny_stack_user", "stacks:global_index": "rule:deny_everybody", "stacks:index": "rule:deny_stack_user", "stacks:list_resource_types": "rule:deny_stack_user", "stacks:list_template_versions": "rule:deny_stack_user", "stacks:list_template_functions": "rule:deny_stack_user", "stacks:lookup": "", "stacks:preview": "rule:deny_stack_user", "stacks:resource_schema": "rule:deny_stack_user", "stacks:show": "rule:deny_stack_user", "stacks:template": "rule:deny_stack_user", "stacks:environment": "rule:deny_stack_user", "stacks:update": "rule:deny_stack_user", "stacks:update_patch": "rule:deny_stack_user", "stacks:preview_update": "rule:deny_stack_user", "stacks:preview_update_patch": "rule:deny_stack_user", "stacks:validate_template": "rule:deny_stack_user", "stacks:snapshot": "rule:deny_stack_user", "stacks:show_snapshot": "rule:deny_stack_user", "stacks:delete_snapshot": "rule:deny_stack_user", "stacks:list_snapshots": "rule:deny_stack_user", "stacks:restore_snapshot": "rule:deny_stack_user", "stacks:list_outputs": "rule:deny_stack_user", "stacks:show_output": "rule:deny_stack_user", "software_configs:global_index": "rule:deny_everybody", "software_configs:index": "rule:deny_stack_user", "software_configs:create": "rule:deny_stack_user", "software_configs:show": "rule:deny_stack_user", "software_configs:delete": "rule:deny_stack_user", "software_deployments:index": "rule:deny_stack_user", "software_deployments:create": "rule:deny_stack_user", "software_deployments:show": "rule:deny_stack_user", "software_deployments:update": "rule:deny_stack_user", "software_deployments:delete": "rule:deny_stack_user", "software_deployments:metadata": "", "service:index": "rule:context_is_admin", "resource_types:OS::Nova::Flavor": "rule:context_is_admin", "resource_types:OS::Cinder::EncryptedVolumeType": "rule:context_is_admin", "resource_types:OS::Cinder::VolumeType": "rule:context_is_admin", "resource_types:OS::Manila::ShareType": "rule:context_is_admin", "resource_types:OS::Neutron::QoSPolicy": "rule:context_is_admin", "resource_types:OS::Neutron::QoSBandwidthLimitRule": "rule:context_is_admin", "resource_types:OS::Nova::HostAggregate": "rule:context_is_admin" } ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/content/0000755000175000017500000000000000000000000023617 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/__init__.py0000644000175000017500000000000000000000000025716 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/0000755000175000017500000000000000000000000026672 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/__init__.py0000644000175000017500000000000000000000000030771 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/panel.py0000644000175000017500000000151100000000000030341 0ustar00jamespagejamespage00000000000000# 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 ugettext_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"),) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/tables.py0000644000175000017500000000235000000000000030516 0ustar00jamespagejamespage00000000000000# 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 ugettext_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(object): name = "resource_types" verbose_name = _("Resource Types") table_actions = (ResourceTypesFilterAction,) multi_select = False ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/tabs.py0000644000175000017500000000221300000000000030173 0ustar00jamespagejamespage00000000000000# 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 ugettext_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,) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/0000755000175000017500000000000000000000000030670 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/resource_types/0000755000175000017500000000000000000000000033743 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/resource_types/_details.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/resource_types/_details.h0000644000175000017500000000036000000000000035677 0ustar00jamespagejamespage00000000000000{% load i18n %}
{{ r_type }}

{% trans "Attributes" %}

{{ r_type_attributes }}
  

{% trans "Properties" %}

{{ r_type_properties }}
  
././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5610838 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/stacks.resource_types/0000755000175000017500000000000000000000000035232 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/stacks.resource_types/_details.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/templates/stacks.resource_types/_de0000644000175000017500000000036000000000000035703 0ustar00jamespagejamespage00000000000000{% load i18n %}
{{ r_type }}

{% trans "Attributes" %}

{{ r_type_attributes }}
  

{% trans "Properties" %}

{{ r_type_properties }}
  
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/urls.py0000644000175000017500000000145500000000000030236 0ustar00jamespagejamespage00000000000000# 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.conf.urls import url from heat_dashboard.content.resource_types import views urlpatterns = [ url(r'^$', views.ResourceTypesView.as_view(), name='index'), url(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='details'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/resource_types/views.py0000644000175000017500000000546300000000000030411 0ustar00jamespagejamespage00000000000000# 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 ugettext_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') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/0000755000175000017500000000000000000000000025107 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/__init__.py0000644000175000017500000000000000000000000027206 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/api.py0000644000175000017500000000541200000000000026234 0ustar00jamespagejamespage00000000000000# 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 heat_dashboard.api import heat from heat_dashboard.content.stacks import mappings from heat_dashboard.content.stacks import sro class Stack(object): pass def d3_data(request, stack_id=''): try: stack = heat.stack_get(request, stack_id) except Exception: stack = Stack() stack.id = stack_id stack.stack_name = request.session.get('stack_name', '') stack.stack_status = 'DELETE_COMPLETE' stack.stack_status_reason = 'DELETE_COMPLETE' 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/forms.py0000644000175000017500000004615600000000000026623 0ustar00jamespagejamespage00000000000000# 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 django from django.conf import settings from django.utils import html from django.utils.translation import ugettext_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 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(object): 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) 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) referenced_files = forms.CharField(label=_('Referenced Files'), widget=forms.widgets.HiddenInput, required=False) if django.VERSION >= (1, 9): # Note(Itxaka): On django>=1.9 Charfield has an strip option that # we need to set to False as to not hit # https://bugs.launchpad.net/python-heatclient/+bug/1546166 environment_data.strip = False template_data.strip = False def __init__(self, *args, **kwargs): self.next_view = kwargs.pop('next_view') super(TemplateForm, self).__init__(*args, **kwargs) def clean(self): cleaned = super(TemplateForm, self).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(object): 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(object): name = _('Preview Template') help_text = _('Select a new template to preview a stack.') class CreateStackForm(forms.SelfHandlingForm): param_prefix = '__param_' class Meta(object): name = _('Create Stack') environment_data = forms.CharField( widget=forms.widgets.HiddenInput, required=False) if django.VERSION >= (1, 9): # Note(Itxaka): On django>=1.9 Charfield has an strip option that # we need to set to False as to not hit # https://bugs.launchpad.net/python-heatclient/+bug/1546166 environment_data.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(CreateStackForm, self).__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, True) if custom_type == 'nova.keypair': return instance_utils.keypair_field_data(self.request, True) if custom_type == 'glance.image': return image_utils.image_field_data(self.request, True) if custom_type == 'nova.flavor': return instance_utils.flavor_field_data(self.request, True) return [] class EditStackForm(CreateStackForm): class Meta(object): 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(object): 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/mappings.py0000644000175000017500000003364400000000000027311 0ustar00jamespagejamespage00000000000000# 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 u'' if isinstance(output, str): parts = urlparse.urlsplit(output) if parts.netloc and parts.scheme in ('http', 'https'): url = html.escape(output) safe_link = u'%s' % (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(u'
%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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/panel.py0000644000175000017500000000135300000000000026562 0ustar00jamespagejamespage00000000000000# 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 ugettext_lazy as _ import horizon class Stacks(horizon.Panel): name = _("Stacks") slug = "stacks" permissions = ('openstack.services.orchestration',) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/sro.py0000644000175000017500000000313000000000000026261 0ustar00jamespagejamespage00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/tables.py0000644000175000017500000003613500000000000026743 0ustar00jamespagejamespage00000000000000# 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 pgettext_lazy from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_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 ungettext_lazy( u"Check Stack", u"Check Stacks", count ) @staticmethod def action_past(count): return ungettext_lazy( u"Checked Stack", u"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 ungettext_lazy( u"Suspend Stack", u"Suspend Stacks", count ) @staticmethod def action_past(count): return ungettext_lazy( u"Suspended Stack", u"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 ungettext_lazy( u"Resume Stack", u"Resume Stacks", count ) @staticmethod def action_past(count): return ungettext_lazy( u"Resumed Stack", u"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 ungettext_lazy( u"Delete Stack", u"Delete Stacks", count ) @staticmethod def action_past(count): return ungettext_lazy( u"Deleted Stack", u"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", u"Init In Progress")), ("init_complete", pgettext_lazy("current status of stack", u"Init Complete")), ("init_failed", pgettext_lazy("current status of stack", u"Init Failed")), ("create_in_progress", pgettext_lazy("current status of stack", u"Create In Progress")), ("create_complete", pgettext_lazy("current status of stack", u"Create Complete")), ("create_failed", pgettext_lazy("current status of stack", u"Create Failed")), ("delete_in_progress", pgettext_lazy("current status of stack", u"Delete In Progress")), ("delete_complete", pgettext_lazy("current status of stack", u"Delete Complete")), ("delete_failed", pgettext_lazy("current status of stack", u"Delete Failed")), ("update_in_progress", pgettext_lazy("current status of stack", u"Update In Progress")), ("update_complete", pgettext_lazy("current status of stack", u"Update Complete")), ("update_failed", pgettext_lazy("current status of stack", u"Update Failed")), ("rollback_in_progress", pgettext_lazy("current status of stack", u"Rollback In Progress")), ("rollback_complete", pgettext_lazy("current status of stack", u"Rollback Complete")), ("rollback_failed", pgettext_lazy("current status of stack", u"Rollback Failed")), ("suspend_in_progress", pgettext_lazy("current status of stack", u"Suspend In Progress")), ("suspend_complete", pgettext_lazy("current status of stack", u"Suspend Complete")), ("suspend_failed", pgettext_lazy("current status of stack", u"Suspend Failed")), ("resume_in_progress", pgettext_lazy("current status of stack", u"Resume In Progress")), ("resume_complete", pgettext_lazy("current status of stack", u"Resume Complete")), ("resume_failed", pgettext_lazy("current status of stack", u"Resume Failed")), ("adopt_in_progress", pgettext_lazy("current status of stack", u"Adopt In Progress")), ("adopt_complete", pgettext_lazy("current status of stack", u"Adopt Complete")), ("adopt_failed", pgettext_lazy("current status of stack", u"Adopt Failed")), ("snapshot_in_progress", pgettext_lazy("current status of stack", u"Snapshot In Progress")), ("snapshot_complete", pgettext_lazy("current status of stack", u"Snapshot Complete")), ("snapshot_failed", pgettext_lazy("current status of stack", u"Snapshot Failed")), ("check_in_progress", pgettext_lazy("current status of stack", u"Check In Progress")), ("check_complete", pgettext_lazy("current status of stack", u"Check Complete")), ("check_failed", pgettext_lazy("current status of stack", u"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(object): 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(object): 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 = '%s/%s' % (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(ResourcesTable, self).__init__( request, data, needs_form_wrapper, **kwargs) self.stack = kwargs['stack'] def get_object_id(self, datum): return datum.resource_name class Meta(object): name = "resources" verbose_name = _("Stack Resources") status_columns = ["status_hidden", ] row_class = ResourcesUpdateRow ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/tabs.py0000644000175000017500000001502000000000000026410 0ustar00jamespagejamespage00000000000000# 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 ugettext_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 = '%s/%s' % (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 = '%s/%s' % (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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/0000755000175000017500000000000000000000000027105 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/0000755000175000017500000000000000000000000030375 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_change_template.html0000644000175000017500000000071300000000000034543 0ustar00jamespagejamespage00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_create.html0000644000175000017500000000032600000000000032666 0ustar00jamespagejamespage00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

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

{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_detail_events.html0000644000175000017500000000004400000000000034246 0ustar00jamespagejamespage00000000000000{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_detail_overview.html0000644000175000017500000000334700000000000034621 0ustar00jamespagejamespage00000000000000{% 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 %}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_detail_resources.html0000644000175000017500000000004400000000000034754 0ustar00jamespagejamespage00000000000000{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_detail_topology.html0000644000175000017500000000044000000000000034616 0ustar00jamespagejamespage00000000000000{% load i18n sizeformat %}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_preview.html0000644000175000017500000000032700000000000033105 0ustar00jamespagejamespage00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

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

{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_preview_details.html0000644000175000017500000000305600000000000034614 0ustar00jamespagejamespage00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_preview_template.html0000644000175000017500000000053100000000000034775 0ustar00jamespagejamespage00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_resource_info.html0000644000175000017500000000046600000000000034272 0ustar00jamespagejamespage00000000000000

{{ 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 }}

././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_resource_overview.html0000644000175000017500000000224600000000000035203 0ustar00jamespagejamespage00000000000000{% 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 }}
  
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_select_template.html0000644000175000017500000000151400000000000034575 0ustar00jamespagejamespage00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_stack_info.html0000644000175000017500000000076400000000000033551 0ustar00jamespagejamespage00000000000000

{{ 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_stack_template.html0000644000175000017500000000013200000000000034416 0ustar00jamespagejamespage00000000000000{% load i18n sizeformat %}
{{ stack_template }}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/_update.html0000644000175000017500000000051100000000000032701 0ustar00jamespagejamespage00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/change_template.html0000644000175000017500000000027700000000000034411 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Change Template" %}{% endblock %} {% block main %} {% include 'project/stacks/_change_template.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/create.html0000644000175000017500000000026300000000000032527 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Launch Stack" %}{% endblock %} {% block main %} {% include 'project/stacks/_create.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/index.html0000644000175000017500000000111400000000000032367 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{{ page_title }}{% endblock %} {% block main %}{{ table.render }}{% endblock %} {% block js %} {% include "horizon/_scripts.html" %} {% endblock %}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/preview.html0000644000175000017500000000026500000000000032747 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Preview Stack" %}{% endblock %} {% block main %} {% include 'project/stacks/_preview.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/preview_details.html0000644000175000017500000000030500000000000034447 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Preview Stack Details" %}{% endblock %} {% block main %} {% include 'project/stacks/_preview_details.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/preview_template.html0000644000175000017500000000030100000000000034631 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Preview Template" %}{% endblock %} {% block main %} {% include 'project/stacks/_preview_template.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/select_template.html0000644000175000017500000000027700000000000034443 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Select Template" %}{% endblock %} {% block main %} {% include 'project/stacks/_select_template.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/templates/stacks/update.html0000644000175000017500000000027600000000000032552 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update Stack Parameters" %}{% endblock %} {% block main %} {% include 'project/stacks/_update.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/urls.py0000644000175000017500000000323700000000000026453 0ustar00jamespagejamespage00000000000000# 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.conf.urls import url from heat_dashboard.content.stacks import views urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^select_template$', views.SelectTemplateView.as_view(), name='select_template'), url(r'^launch$', views.CreateStackView.as_view(), name='launch'), url(r'^preview_template$', views.PreviewTemplateView.as_view(), name='preview_template'), url(r'^preview$', views.PreviewStackView.as_view(), name='preview'), url(r'^preview_details$', views.PreviewStackDetailsView.as_view(), name='preview_details'), url(r'^stack/(?P[^/]+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P[^/]+)/change_template$', views.ChangeTemplateView.as_view(), name='change_template'), url(r'^(?P[^/]+)/edit_stack$', views.EditStackView.as_view(), name='edit_stack'), url(r'^stack/(?P[^/]+)/(?P[^/]+)/$', views.ResourceView.as_view(), name='resource'), url(r'^get_d3_data/(?P[^/]+)/$', views.JSONView.as_view(), name='d3_data'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/stacks/views.py0000644000175000017500000003122500000000000026621 0ustar00jamespagejamespage00000000000000# 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 ugettext_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(IndexView, self).__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 stack 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(SelectTemplateView, self).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(ChangeTemplateView, self).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(ChangeTemplateView, self).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(PreviewTemplateView, self).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: initial['parameters'] = json.dumps(self.kwargs['parameters']) return initial def get_form_kwargs(self): kwargs = super(CreateStackView, self).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(EditStackView, self).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(EditStackView, self).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( PreviewStackDetailsView, self).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(DetailView, self).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(ResourceView, self).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") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/0000755000175000017500000000000000000000000027500 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/__init__.py0000644000175000017500000000000000000000000031577 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/api.py0000644000175000017500000002074000000000000030626 0ustar00jamespagejamespage00000000000000# 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 = error.message 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.zoneName, 'name': az.zoneName} 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(object): 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 = [], error.message 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, '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, '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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/panel.py0000644000175000017500000000152100000000000031150 0ustar00jamespagejamespage00000000000000# 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 ugettext_lazy as _ import horizon class TemplateGenerator(horizon.Panel): name = _("Template Generator") slug = 'template_generator' permissions = ('openstack.services.orchestration',) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/templates/0000755000175000017500000000000000000000000031476 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/templates/template_generator/0000755000175000017500000000000000000000000035357 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/templates/template_generator/index.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/templates/template_generator/in0000644000175000017500000001617100000000000035716 0ustar00jamespagejamespage00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/urls.py0000644000175000017500000000144000000000000031036 0ustar00jamespagejamespage00000000000000# 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.conf.urls import url from heat_dashboard.content.template_generator import views urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), url(r'^get_resource_options$', views.OptionView.as_view(), name="apis"), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_generator/views.py0000644000175000017500000000213400000000000031207 0ustar00jamespagejamespage00000000000000# 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 ugettext_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") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/0000755000175000017500000000000000000000000027362 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/__init__.py0000644000175000017500000000000000000000000031461 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/panel.py0000644000175000017500000000152500000000000031036 0ustar00jamespagejamespage00000000000000# 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 ugettext_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"),) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/tables.py0000644000175000017500000000336300000000000031213 0ustar00jamespagejamespage00000000000000# 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 ugettext_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(object): 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(object): name = "template_functions" verbose_name = _("Template Functions") table_actions = (tables.FilterAction,) multi_select = False ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/tabs.py0000644000175000017500000000345500000000000030674 0ustar00jamespagejamespage00000000000000# 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 ugettext_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,) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/0000755000175000017500000000000000000000000031360 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/stacks.template_versions/ 28 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/stacks.template_versio0000755000175000017500000000000000000000000035772 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000022400000000000011453 xustar0000000000000000126 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/stacks.template_versions/_details.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/stacks.template_versio0000644000175000017500000000004400000000000035772 0ustar00jamespagejamespage00000000000000{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000022100000000000011450 xustar0000000000000000123 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/stacks.template_versions/index.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/stacks.template_versio0000644000175000017500000000024100000000000035771 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Template Versions" %}{% endblock %} {% block main %} {{ table.render }} {% endblock %} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5650837 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/template_versions/0000755000175000017500000000000000000000000035123 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000119 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/template_versions/_details.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/template_versions/_det0000644000175000017500000000004400000000000035757 0ustar00jamespagejamespage00000000000000{% load i18n %} {{ table.render }} ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/template_versions/index.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/templates/template_versions/inde0000644000175000017500000000024100000000000035762 0ustar00jamespagejamespage00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Template Versions" %}{% endblock %} {% block main %} {{ table.render }} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/urls.py0000644000175000017500000000146700000000000030731 0ustar00jamespagejamespage00000000000000# 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.conf.urls import url from heat_dashboard.content.template_versions import views urlpatterns = [ url(r'^$', views.TemplateVersionsView.as_view(), name='index'), url(r'^(?P[^/]+)/$', views.DetailView.as_view(), name='details'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/content/template_versions/views.py0000644000175000017500000000424500000000000031076 0ustar00jamespagejamespage00000000000000# 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 ugettext_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') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/0000755000175000017500000000000000000000000023537 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/_1610_project_orchestration_panel.py0000644000175000017500000000220100000000000032503 0ustar00jamespagejamespage00000000000000# 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 ugettext_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 } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/_1620_project_stacks_panel.py0000644000175000017500000000201600000000000031114 0ustar00jamespagejamespage00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/_1630_project_resource_types_panel.py0000644000175000017500000000217200000000000032703 0ustar00jamespagejamespage00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/_1640_project_template_versions_panel.py0000644000175000017500000000205600000000000033375 0ustar00jamespagejamespage00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/_1650_project_template_generator_panel.py0000644000175000017500000000703200000000000033513 0ustar00jamespagejamespage00000000000000# 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 = [ 'horizon/lib/bootstrap_scss/scss/_bootstrap.scss', 'horizon/lib/font_awesome/scss/font-awesome.scss', '%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') ]) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/enabled/__init__.py0000644000175000017500000000000000000000000025636 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/exceptions.py0000644000175000017500000000136500000000000024705 0ustar00jamespagejamespage00000000000000# 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, ) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/local_settings.d/0000755000175000017500000000000000000000000025401 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/local_settings.d/_1699_orchestration_settings.py0000644000175000017500000000230200000000000033403 0ustar00jamespagejamespage00000000000000# 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.json', }) # 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/local_settings.d/__init__.py0000644000175000017500000000000000000000000027500 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/0000755000175000017500000000000000000000000023404 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/cs/0000755000175000017500000000000000000000000024011 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/cs/LC_MESSAGES/0000755000175000017500000000000000000000000025576 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/cs/LC_MESSAGES/django.po0000644000175000017500000002522400000000000027405 0ustar00jamespagejamespage00000000000000# 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 0.0.1.dev938\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-12-07 18:26+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 3.9.6\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 list." msgstr "Nelze získat seznam zásobníků." 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" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/de/0000755000175000017500000000000000000000000023774 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/de/LC_MESSAGES/0000755000175000017500000000000000000000000025561 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/de/LC_MESSAGES/django.po0000644000175000017500000003456700000000000027402 0ustar00jamespagejamespage00000000000000# 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 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-25 02:40+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" #, 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 "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 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 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 list." msgstr "Stackliste 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/de/LC_MESSAGES/djangojs.po0000644000175000017500000000626300000000000027727 0ustar00jamespagejamespage00000000000000# 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." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_AU/0000755000175000017500000000000000000000000024373 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_AU/LC_MESSAGES/0000755000175000017500000000000000000000000026160 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_AU/LC_MESSAGES/django.po0000644000175000017500000002370500000000000027771 0ustar00jamespagejamespage00000000000000# 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: 2018-02-23 08:04+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 list." msgstr "Unable to retrieve stack list." 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" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_GB/0000755000175000017500000000000000000000000024356 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_GB/LC_MESSAGES/0000755000175000017500000000000000000000000026143 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_GB/LC_MESSAGES/django.po0000644000175000017500000003261700000000000027756 0ustar00jamespagejamespage00000000000000# 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 msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2018-05-15 05:59+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-05-15 10:04+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 "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 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 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 list." msgstr "Unable to retrieve stack list." 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 "" "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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/en_GB/LC_MESSAGES/djangojs.po0000644000175000017500000000565300000000000030313 0ustar00jamespagejamespage00000000000000# 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." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/es/0000755000175000017500000000000000000000000024013 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/es/LC_MESSAGES/0000755000175000017500000000000000000000000025600 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/es/LC_MESSAGES/django.po0000644000175000017500000002366300000000000027414 0ustar00jamespagejamespage00000000000000# Eddie Ramirez , 2016. #zanata # Eugènia Torrella , 2016. #zanata # Jose Porrua , 2016. #zanata # Carlos Camacho , 2017. #zanata # Zeus Arias Lucero , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: heat-dashboard 0.0.1.dev938\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-12-07 18:26+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-24 11:28+0000\n" "Last-Translator: Zeus Arias Lucero \n" "Language-Team: Spanish\n" "Language: es\n" "X-Generator: Zanata 3.9.6\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 "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 list." msgstr "No se ha podido recuperar la lista de pilas." 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" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/fr/0000755000175000017500000000000000000000000024013 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/fr/LC_MESSAGES/0000755000175000017500000000000000000000000025600 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/fr/LC_MESSAGES/django.po0000644000175000017500000003432700000000000027413 0ustar00jamespagejamespage00000000000000# 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: 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: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 list." msgstr "Impossible de récupérer la liste des piles." 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/fr/LC_MESSAGES/djangojs.po0000644000175000017500000000665100000000000027747 0ustar00jamespagejamespage00000000000000# 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." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/id/0000755000175000017500000000000000000000000024000 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/id/LC_MESSAGES/0000755000175000017500000000000000000000000025565 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/id/LC_MESSAGES/django.po0000644000175000017500000003276700000000000027406 0ustar00jamespagejamespage00000000000000# 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-13 03:39+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 list." msgstr "Tidak dapat mengambil daftar stack." 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/id/LC_MESSAGES/djangojs.po0000644000175000017500000000516200000000000027730 0ustar00jamespagejamespage00000000000000# 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." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/it/0000755000175000017500000000000000000000000024020 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/it/LC_MESSAGES/0000755000175000017500000000000000000000000025605 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/it/LC_MESSAGES/django.po0000644000175000017500000002342700000000000027417 0ustar00jamespagejamespage00000000000000# 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 0.0.1.dev938\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-12-07 18:26+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 3.9.6\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 list." msgstr "Impossibile richiamare l'elenco degli stack." 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" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ja/0000755000175000017500000000000000000000000023776 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ja/LC_MESSAGES/0000755000175000017500000000000000000000000025563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ja/LC_MESSAGES/django.po0000644000175000017500000003602400000000000027372 0ustar00jamespagejamespage00000000000000# 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:02+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 list." 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 "テンプレート" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ja/LC_MESSAGES/djangojs.po0000644000175000017500000000576100000000000027733 0ustar00jamespagejamespage00000000000000# 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\" を選択しました。削除されたスタックは元に戻せません。" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ko_KR/0000755000175000017500000000000000000000000024411 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ko_KR/LC_MESSAGES/0000755000175000017500000000000000000000000026176 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ko_KR/LC_MESSAGES/django.po0000644000175000017500000004346700000000000030016 0ustar00jamespagejamespage00000000000000# 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: 2019-07-17 13:53+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" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django #, python-format msgid "%(resource_status)s: %(resource_status_reason)s" msgstr "%(resource_status)s: %(resource_status_reason)s" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django #, 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로 불러옵니다." # auto translated by TM merge from project: ironic-ui, version: master, DocId: ironic_ui/locale/djangojs 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 "캔버스 삭제" # auto translated by TM merge from project: horizon, version: master, DocId: horizon/locale/djangojs 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 생성" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django 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 삭제함" # auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs msgid "Description" msgstr "설명" # auto translated by TM merge from project: tripleo-ui, version: master, DocId: i18n/messages msgid "Description:" msgstr "설명:" msgid "Direct Input" msgstr "직접 입력" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/djangojs msgid "Disabled" msgstr "비활성화됨" msgid "Edit Template" msgstr "템플릿 편집" msgid "Enable rollback on create/update failure." msgstr "생성/업데이트 실패시 롤백 사용." # auto translated by TM merge from project: murano-dashboard, version: master, DocId: muranodashboard/locale/django 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 "기능" # auto translated by TM merge from project: designate-dashboard, version: master, DocId: designatedashboard/locale/django 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 "초기화 진행 중" # auto translated by TM merge from project: sahara-dashboard, version: master, DocId: sahara_dashboard/locale/django msgid "Last Updated" msgstr "마지막 업데이트 날짜" msgid "Launch" msgstr "실행" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "Launch Parameters" msgstr "실행 매개 변수" msgid "Launch Stack" msgstr "Stack 시작" # auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs msgid "Links" msgstr "링크" msgid "Load Draft" msgstr "초안 불러오기" msgid "Manage Drafts" msgstr "초안 관리" # auto translated by TM merge from project: Freezer Horizon Web UI, version: master, DocId: disaster_recovery/locale/django msgid "Minutes" msgstr "분" # auto translated by TM merge from project: designate-dashboard, version: master, DocId: designatedashboard/locale/django 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 "다음" # auto translated by TM merge from project: openstack-manuals, version: stable-liberty, DocId: doc/install-guide/source/locale/install-guide msgid "Orchestration" msgstr "Orchestration" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "Outputs" msgstr "출력" # auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs msgid "Overview" msgstr "개요" # auto translated by TM merge from project: tripleo-ui, version: master, DocId: i18n/messages 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 "템플릿 미리보기" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "Preview a new stack with the provided values." msgstr "제공된 값을 가지는 새 스택을 미리 봅니다." # auto translated by TM merge from project: ironic-ui, version: master, DocId: ironic_ui/locale/djangojs msgid "Properties" msgstr "속성" msgid "Referenced Files" msgstr "참조된 파일들" msgid "Resource" msgstr "리소스" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "Resource ID" msgstr "리소스 ID" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django 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 재개됨" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django 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 이름 =" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "Stack Parameters" msgstr "스택 매개 변수" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "Stack Preview" msgstr "스택 미리보기" msgid "Stack Resource" msgstr "스택 리소스" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django 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 "이벤트 이후 시간" # auto translated by TM merge from project: octavia-dashboard, version: master, DocId: octavia_dashboard/locale/djangojs msgid "Timeout" msgstr "제한시간" msgid "Topology" msgstr "토폴로지" # auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs 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 list." msgstr "Stack 목록을 찾지 못했습니다." 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 "템플릿 버전을 찾지 못했습니다." # auto translated by TM merge from project: zun-ui, version: master, DocId: zun_ui/locale/djangojs 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 매개변수 업데이트" # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django 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 "" "이 스택을 변경할 때 사용될 템플릿을 지정하기 위하여 사용할 수 있는 템플릿 소" "스 옵션 중 하나를 사용합니다." # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django msgid "" "Use one of the available template source options to specify the template to " "be used in creating this stack." msgstr "" "스택을 만드는데 필요한 템플릿을 지정하는데 사용할 수 있는 템플릿 소스 옵션 " "중 하나를 사용합니다." # auto translated by TM merge from project: horizon, version: stable-mitaka, DocId: openstack_dashboard/locale/django 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ko_KR/LC_MESSAGES/djangojs.po0000644000175000017500000000553500000000000030345 0ustar00jamespagejamespage00000000000000# 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은 복구할 수 없습니다." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ne/0000755000175000017500000000000000000000000024006 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ne/LC_MESSAGES/0000755000175000017500000000000000000000000025573 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ne/LC_MESSAGES/django.po0000644000175000017500000005213700000000000027405 0ustar00jamespagejamespage00000000000000# 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: 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-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 list." 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 "टम्प्लेट" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ne/LC_MESSAGES/djangojs.po0000644000175000017500000001064000000000000027733 0ustar00jamespagejamespage00000000000000# 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\" चयन गर्नुभएको छ। हटाइएको स्ट्याकहरू पुन: प्राप्ति योग्य छैन।" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pl_PL/0000755000175000017500000000000000000000000024412 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pl_PL/LC_MESSAGES/0000755000175000017500000000000000000000000026177 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pl_PL/LC_MESSAGES/django.po0000644000175000017500000002342300000000000030005 0ustar00jamespagejamespage00000000000000# 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: 2018-02-23 08:04+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 list." msgstr "Nie można pobrać listy stosów." 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" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pt_BR/0000755000175000017500000000000000000000000024412 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pt_BR/LC_MESSAGES/0000755000175000017500000000000000000000000026177 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pt_BR/LC_MESSAGES/django.po0000644000175000017500000003443500000000000030012 0ustar00jamespagejamespage00000000000000# 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: 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: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 list." msgstr "Não é possível recuperar lista de pilhas." 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" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/pt_BR/LC_MESSAGES/djangojs.po0000644000175000017500000000632500000000000030344 0ustar00jamespagejamespage00000000000000# 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." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ru/0000755000175000017500000000000000000000000024032 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ru/LC_MESSAGES/0000755000175000017500000000000000000000000025617 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ru/LC_MESSAGES/django.po0000644000175000017500000004430100000000000027423 0ustar00jamespagejamespage00000000000000# 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 msgid "" msgstr "" "Project-Id-Version: heat-dashboard VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-08-05 13:28+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-05 09:50+0000\n" "Last-Translator: Roman Gorshunov \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) ссылка для скачивания образа." 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 "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 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 "Содержимое шаблона." #, 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 list." 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 "шаблон" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/ru/LC_MESSAGES/djangojs.po0000644000175000017500000001022700000000000027760 0ustar00jamespagejamespage00000000000000# OpenStack Infra , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Maxim Bozhenko , 2016. #zanata # Roman Gorshunov , 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-08-05 13:28+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-05 09:56+0000\n" "Last-Translator: Roman Gorshunov \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\". Удаленные стеки не подлежат восстановлению." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5490837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/tr_TR/0000755000175000017500000000000000000000000024436 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/tr_TR/LC_MESSAGES/0000755000175000017500000000000000000000000026223 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/tr_TR/LC_MESSAGES/django.po0000644000175000017500000002475700000000000030044 0ustar00jamespagejamespage00000000000000# 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: 2018-02-23 08:04+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 list." msgstr "Yığın listesi 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" ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_CN/0000755000175000017500000000000000000000000024405 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_CN/LC_MESSAGES/0000755000175000017500000000000000000000000026172 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_CN/LC_MESSAGES/django.po0000644000175000017500000003165400000000000030005 0ustar00jamespagejamespage00000000000000# 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: 2019-03-18 17:15+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 list." 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 "模板" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_CN/LC_MESSAGES/djangojs.po0000644000175000017500000000505000000000000030331 0ustar00jamespagejamespage00000000000000# 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: 2019-03-18 17:15+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”。删除的堆栈无法被恢复。" ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_TW/0000755000175000017500000000000000000000000024437 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5690837 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_TW/LC_MESSAGES/0000755000175000017500000000000000000000000026224 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/locale/zh_TW/LC_MESSAGES/django.po0000644000175000017500000002274200000000000030035 0ustar00jamespagejamespage00000000000000# Zhang Xiaowei , 2015. #zanata # Andreas Jaeger , 2016. #zanata # Jennifer , 2016. #zanata # Gene Kuo , 2017. #zanata # Terry Tai , 2017. #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: 2018-02-23 08:04+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-09-16 08:03+0000\n" "Last-Translator: Gene Kuo \n" "Language-Team: Chinese (Taiwan)\n" "Language: zh_TW\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" msgid "A local environment to upload." msgstr "要上傳的本端環境。" msgid "A local template to upload." 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 "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] "已檢查堆疊" 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 "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 "Direct Input" msgstr "直接輸入" msgid "Edit Template" msgstr "編輯範本" msgid "Enable rollback on create/update failure." msgstr "啟用在建立/更新失敗時回復" msgid "Environment Data" msgstr "環境資料" msgid "Environment File" msgstr "環境檔案" msgid "Environment Source" msgstr "環境來源" msgid "Events" msgstr "事件" msgid "File" msgstr "檔案" msgid "Function" 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 "Launch" msgstr "啟動" msgid "Launch Stack" 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 "Overview" msgstr "概觀" #, python-format msgid "Password for user \"%s\"" msgstr "使用者 \"%s\" 的密碼" #, 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 "Resource" 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] "已回復堆疊" 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 "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 " msgctxt "current status of stack" msgid "Snapshot Failed" msgstr "建立 Snapshot 失敗" msgctxt "current status of stack" msgid "Snapshot In Progress" msgstr "正在建立 Snapshot " msgid "Stack Events" msgstr "堆疊事件" msgid "Stack ID" msgstr "堆疊 ID" msgid "Stack ID =" msgstr "堆疊 ID =" msgid "Stack Name" msgstr "堆疊名稱" msgid "Stack Name =" msgstr "堆疊名稱 =" msgid "Stack Resource" msgstr "堆疊資源" 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 "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 Source" msgstr "範本來源" msgid "Template URL" msgstr "範本 URL" 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 "Topology" msgstr "拓撲" msgid "Type" msgstr "類型" msgid "Type =" msgstr "類型 =" msgid "URL" msgstr "網址" #, 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 "無法擷取 meta 資料。" msgid "Unable to retrieve resource type details." msgstr "無法擷取資源類型詳細資料。" msgid "Unable to retrieve resource." msgstr "無法擷取資源。" msgid "Unable to retrieve stack list." 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 "Updated" msgstr "已更新" msgid "Version" msgstr "版本" msgid "You must specify a template via one of the available sources." msgstr "您必須透過一個可用的來源來指定範本。" msgid "environment" msgstr "環境" msgid "template" msgstr "範本" ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/0000755000175000017500000000000000000000000023434 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/0000755000175000017500000000000000000000000025363 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/0000755000175000017500000000000000000000000027031 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/0000755000175000017500000000000000000000000031761 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5810838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/0000755000175000017500000000000000000000000032535 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-gray.gif0000644000175000017500000001056100000000000035263 0ustar00jamespagejamespage00000000000000GIF89a,,񙙙٩¡պ!2! NETSCAPE2.0,,, dihlp,tmx|pH,Ȥrl:ШtJZجvz80zMzN ~odh|3bVKuAk8l6fi/I%Tij^ˤoӔmяyڄrߔo׊bܔ潙ΓXPPʤ#CD>#")b1 D*PHV~4!i$E&Y,ʖ*_¤!s抚6[\W-}FOJg$:5*MF@AM\qs睰>x4 1Ŋp$0޽ @" 6nb 4؈cY+ < P6I2w :4RlcnLN~ ;6gOkn۴o>)sSÑ}Bԫ/8PvK"-x*~Tp0ۻ|vw_ E'iqa^ BF(aO`ha ~WLX "!敨ʼnGn:./>7ֈwE1z G.n`_,PD97 1%X v7eI1yCϕ[Ve< 0A8 T. aYg4BIgH 8P$l1 ۛ1zh< O)1v@ԡd@ܙ֨J-Dkgp+/ ArjC]^:/DꪱV Qd)u4KGoU (.K2/Q듹־ʻ.{/0軯7óۯ:|C|.R.Aqܰ.|Ic[[ƫz2Y2 3y3 S@(sU&-e!0tN㔳KcqVS4u5]gBW!6J6^3]{̝ImGuܳ Pi 0 wP&uTOɪ[nh^f_nwޢwy韟yt8=*®٬ ^v^̻1yn3çc=cOv]?dIڏ`c_ h>Owi`1zYAߏ?MJgKW]]uo}"8G N .Ӡ1 H#lL:h+`\XAƐACN e $"чc"âxPVbE*b^L WE%.j0 <O<d ggcF 0z X =Q}D!CdCK1 b%")HE-l'EzHtM@IFTqL# QŔT- XR$TJ01uT&]K!,&WFtAHCJґ(=JSҕ0U>Pnħ0Q.TvhOw#j<pE)SjBYSa*!Y bvA[*\+c]XK~l3yJbLϢufb/fZApа-$;܏URP?k7C9}h^QTn:ui3 4?Ps& 6E˜O~͘y܆QUe[]-f _r3q `נZUnⵄ+\.Ŏ0 ~N{`bўO81n7b<<[8*.87`klc$wLnc1y"e-|)\W-9f{f\ gBDo\P-PG tiP]jUW&ĨuYӺW^?jW6VtX9mCpa"9*chM_$wm)䋃0=^$ƞ4H"nj?v'd! 2B$K^P9ID>6E9Ɯ\ ђ8R*N1E̊STaԭ-j*N]=&l۞y 'b\sm{,`Tg!{FixG=rGdG;{}hK>V:vz=d67ܼey no">}k/G'Gz';DG :}FAhz`Yס#r"bxn,.a@-˜Y80#P9?!Y P@\Bu bc(^GZ# &EDiKigu5J*aFYpU䢌ŸQVe:Q)kjiH,j$ TZ Zꭧ*, \%e"lF;TkURv;:fW.f@^MK!/c4 1&XƒZiJW>q cq~o8צA,011lެ3 r^ACtc 1OڪXUo0EuTת-do%i*mt-Hmjm7½}x=M∷ٍO ߃-xg}9 Z:7޻t 9֩x搷Nw]׆.;b Wwtkq׉|ʓ{:o,^cOM~v?{-gP46?߾/nUCP? I:sV֬' rPg߲BA{%L={/m!Ġyo'\dXP_9L a+(΂z"h-!̳"+rΈ[7Tb802)$:Sg߾^0Pz#xD?yk!-AF XXsc~D$/h5α@ɼ2FB Ք(qHl+QKYVrQ|'ߜ?*YL~Aj&}9KhV,evLfA$4yj*&7 S94QmMR=X'M?K$%$3IKs΂jʧ>7CP hZO5_ FFNƣ(`@ʜ)9Ii!Ґt-K5:SĔ 7= J+"fdCͩH ũlQ)S_էHU9 Sn,\ Ed2Vє#d\԰f)[e:Z"THw4 Z(ͶW+1nzO$tpʄ~@تv!&R 8Q;FmC`ں6eVbK[mh{uVmր-ܚV]h nz xKM2";././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-gray.svg0000644000175000017500000000434500000000000035320 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-green.sv0000644000175000017500000000434500000000000035307 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/alarm-red.svg0000644000175000017500000000434500000000000035130 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/apple-touch-icon.png 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/apple-touch-ic0000644000175000017500000000163300000000000035275 0ustar00jamespagejamespage00000000000000PNG  IHDR gAMA a cHRMz&u0`:pQ<PLTEhxFY1G/D0Eiy2H42fv턑,Bar4Ugkz 8BUL_%<3쀎n~n}5br~'=w*@"9]n79NEY텒탐QcRdPbsm|_p0F *IDATxR@@ JTP wqE\po|iI{ Jz"""""e#m$ˍ5ǦBqz ?[,4ϕ+*X|-Sm\_dP47i-6t%qfĻk9d&][[V[6ںH )a{wl2۲?mЂ 4hРA 4.Rhoiڹ㌛\(ɩg坞>;8=F뜟%^8>&9?]&;GF_[I A-pSw8GwO-d i!&06ӳ7ח>=Zow@ 4hРA 4hРA O5ZI3""__ P9}rED-Sr\#~|2`0hРA 4hРA;DkAV]*oEyʛUީz{TndйB\)g}sfco1xS"v~xp , Y5NIENDB`././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-gray.gi0000644000175000017500000001304100000000000035262 0ustar00jamespagejamespage00000000000000GIF89a,,ظʓЊհ˺ðȵ䘻пɸȨų߿ψǶ!2! NETSCAPE2.0,,,@pH,Ȥrl:ШtJZجvzxL.zn|N~x  w  _KǨ̰~q g ]Hԁl+h*,5@AP MeH CC(vl7PAo? hЯ&y8$@$€*W`iX k Xk `잙\Nv̩L8@/9 5j6(id 064>€[\ ph׶baIps|h\#0&¥1xDpԍ b˺A|iF|?}ǨǷyhwt[}ja]1Jn r hFd2XgE1y|H02sx1"v4.1OLW#kBh}PBї1`i@"@%8@x% )1%~Ō3-ezi^ .:Ɇ4cL[v1!)ƒ˟qq0KLJiD%my &a'rxjڪ.sȜz~ˏ.11(Kxfr@z[q,.~@kwj v˧QHxlK楋KXYG:[%Qj)~$ Z.1c.!1 G5G(?r|j)ǼB'ˬ978qܸnD7]aҹdAq)Q(Zئ0LgU]6G0)Ė!B.6w?6/HϦ!-msBmI.^ۢ ˂c.=hjTl*9皇zϧ47.":>!n)q{p!S滐a.)a6˻)|kA⢀*:Hc6Yd-^m.ram)š:쳡~Ipx hIH_M:S$ p:7"5{c 65Lw]A坢nT@.F5Bvp "BBZpt%| 1XCp_9tB '1{<$)"g!"tR4D1.݂0ԕD) x9Ũ_"F%{( 9FbcǬ+|c f,΁_d²!(d0y\<4I౩kPbQ"2PIqRzd.0JRޱE_pyTuR (80Ҕ4BY1kR[+oy /7Ii@`F@>!g8(,@\䎕\sMeI@}9eZ ](j 4~'FiJIEjN۠1c1`R7t4EH6dv೏4i)CQӔ&Q^NUUñ0*W3nLXߠlfG`V HfXNLޔkj fYHƃ1-)^T>`)Cajmyn4ɣ",bPLqbc8pd-)PTRq|޸nkp*BxM_ ;⟝Eu VAt x&zX}z^iP4ewEB@a~`Z ._[8`1S:vm@( BV"B%?aA8.zb)isq_rW x1GPF8O>]Q,ٕMrۍ J(Px~E/ʛ ^3;YGCA]lo?ha2-i@ac$ M Qet&0MYIBAĞE@,Ӆڼ~U_Eޚhxg3 @^),Ue ׽nóGނ̍z;n0U ,nQ_t ov8V::&:k oۡk'PinZw]\(Q}k>T9&)L4y6_E"`=5Kf-r!e98wjB|iK' G?|('7.osy~faw;sc3dJׂC{Pu niSOROfS-wY_^|V[pkKM4UkSzC? v!ʗ*dϿ%w^ǵzz~h@{ImxxXuޗ}'EcS]0is0Hw$|+X2ǂott`PrZYf(8'G}<#CYv!Pyw؄rG|T@8wJU`?R\Ff gE~Le]H`S5Wf z8˂b4GphgX h'FnvN@Of TXvqx1gJYwLqSx/xBozXt7 4uvfeoP؋l`wTXèPGw>x|@jϤֆW،r`}8&ψ8l6v7{D$w(3O=(\Gpv08`fy0}I"Lypn뒑dq 7Hrd,siK3IX TP% &L@~J" 2B 9 :e..GЉ 0LKГ>z3 i bdjiDO( Nww_q4BLǠs yIb,vy$9NielC&y 0) ^7P " ؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-green.s0000644000175000017500000000577500000000000035302 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/config-red.svg0000644000175000017500000000577500000000000035311 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/db-gray.gif0000644000175000017500000002630200000000000034554 0ustar00jamespagejamespage00000000000000GIF89a\\ɾǾ˵װѷָ󟟟޲쫫̙嫭𥥥ػɿǶƢΜȼӬͭ! NETSCAPE2.0! XMP DataXMPA?xpackeA9C02C3" xmpMM:InstanceID="xmp.iid:Ba> !2,\\H*\ȰÇ#JH/ҨȱBBDIMѣˈVDbt͒'B7I6D A##&EJ ĉX1#ȚdZ)T i] N2r4,Agsm˗0'$|˸g9!Q33~ ?|%9a|D2.eP AMZ^׊Ʃ ++o`8.w/NVB^(و2*NGZÈ]HIHF"Pt fI 6#BǐVa|E}Xas$MWha\Ёxx6MAD]ƐmmDLW2._X$ Xf[&K(7%"*&$`$MH"\ftp"%F[Y&Em1 BYHmH-~FDYۃQ'-X.R# R-Q"NtU,"jm,Bu ZIB : da"IM*p.bT@!&pB(Xp_ /I 6&;K]@hA-pUrD@apf!!5"A!BQ`$ZuHC\ ìsɸ7!iBъr? +rM>@OURu )yMЌKl_Z׌ȱoۋy!45"[ $rĈ$7u;<$Eb7ĵ["ZPwc.t5Zר}\PFMDM+4M=oJ{SzmdtBl^{J/nAp {IZ r9jd,B$ Ί=E{Э> e$~VsհAҫH 89z$)|(I"w!}rN%dfNBU|\BNS.S c%TP2,,5QJ>90';hO@r=d%T(S0ZhA!,''HPA>dY8FcZPD6ñǏPc@iʀ\rӴIcIE@[ Jc2=:H JYIc$4:&ɯIhp 6 %(ućpEbLr= g+0&Nf3~0L\ƌ9-g 3ΘgׯU4mdΌ0i ۰ٜy.f&d@v[/@1f˪E/d4,~q-5ͺuvM{qMQ۵; 4*>c ~DhX(q2v<qޯ ~bsfמS/z ͬa1l $ x& vg&ws|1 wW`ܥ ]&0q-'b+^~ycnhBh&x Wl _iJ. C 'WۄU ?#r!,''HpĒ XYhV4d6±Ǐkt9j\r~Ѳ͎5r-K| m УsPlQeM&aKL]@fY(1II8Kw&ZLȭ.Mdoߺ$ 1c/41ܗCC8 @^\b|ެūc˖z`سs2ă 融eHv@-\6&6m͋<;bnl֒_yh rs֯ 'j | uf <FpZyMM(b^Yxv)ͩXqIXIи}&Zi(Gk=8 nWH6c^0]$at_wd~EZ9W@!,''=HP@ `XaA6Ǐ/@ .\rrhв͎ V@vW % rt iG]_!!"VHv &^ةhͩXq  r6i8GREX%nsxbMwLGW 1Gsm_V!,''H0$IV(YGJ$ 6Ǐ \re$$xв͎LLq# | m У PQ%M"y` LU`$ɑ @X(IIbKWJȭCF  ޾tI !28p@ q/y<fΜ@`DFYRƬc˖zسsVID]v,I-\vK[={%o=pVBd >(BN?خp>V@#W  rFDt iG]_ "VHv &ةhͩXqHHr6i8bGREX%nxMKLGHAsm_V!,''HÈ'OX#E1<06LǏ@<\re'yII3@[ J#D=:CJYa:cɯ#W?p@pMBVs1&w!cs>aS79bth1w0[^ܹ~>?~&}QցyЄ| \^ThaхvYvF8ǡI!1xԅpey؅:| ]T@"A!1ZĄ*`qP2Wx[!,''HD$H$tXB 6ǏH@4\re$iIIH@[ J# =:0JY>a:B@#ɯ 10$ %(DpBrE&ěH+&Nf\ƌ HMΘ(ׯU7m 6g!HH4LCm<7li߯%v@Ir.] ;Q7p&Y 'ˀf؟? ͮUl!=~bF a4X&h.|wty†n Fֹ^t%lWwSL7W y'q @gX }W:APHq0iM.ES0m2%xA#p!,''H. YdIH6\±Ǐ8N\reO:iIf'.6ɳ=SQNppThҁ l򉓘C8)J&~B ,d"ʝ+$E(M/]!)!+ )s) $zM(;K Bqװ_u۝4ܱ1eÎ"{0<.^{X7{6&ۅWysRH/lֶo<[ F%`_vUEw&W Pda ^d]Q'v!!t!Eop! )Y~6ZdjhZk"A2D)`%W !Ha~y\>cr!,''HxXaB|@łȱǏ8(0Ǔ(Q b)cAƙ8S gJ7} @DC 4&Q$BE"I$@|AGI$ٳK[<][Q΅ػJh a|CH #nkX˘!R4g,l9@ݷ%HpP9be-ƾp൛m ᇿ/f\"TPСo]$sw~=lԹGXA'8۬^K> ۿX".qĦe_j^D}| 0e(u8!XqAdEɭ]_ tbՆV@!,''AH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜIM!,''HxXaB|@6Ǐ p@ ST)"PIk\Ο+J4-(S$34"XI0Ճ %Vz%ٳh/(၃ٴp/$%+D׺XBoZ%e`q`!$rL9r!r88ρ7< g%v68^X r\:XEFvmwݨó]h~tdرs]psL3j=|fE(>s ^"_˟l^"HUr1X c]GX0my7yg{ o`VsnFtQYbWQoi!,''H. YdIHł.ȱGOqQ'SLE%IIS@WF'J80@ P2UNNEbIP0& % Y׷p-Cܻm]4Ҥ xۗoSx SY&SHXQ.["ӨOvl:kH\u(.B%ڨlbPH40׃Iv\Z=+W "{oEPVk]AǽU 6ntg^Mx .ds$fYg9`j)] h#j H RdSxE% YEq^>~!,''HD$H$tXB 6ǏH@4\re$$Ѳ͎LrM#| m Уi !PCQMajL @&Y(qDI$JKw.Bȭ.7!F`oߺ% 1c7S0ܷDH8 @n0@⤴i%|<@ūc˖:*ٸ? i21 !gK@"]˖0I&ay[GoϪ=Șf!4LO,ϝgbi$ n@4܁m(@Κa62h7w6VԲ {tW f8F5o|xEtabtIGw~ygu%~əXp ^ qh}j-_ mwHPFb}4\\ (`Vas[7d}AJ W@!,''H0$IV(YGJ$ 6Ǐ \re$I#RI@[ J+=:PJY2y`$:P@ɯ $90 %")׷pzdIܻp? 2b_A _d#" /ʕ+Ϡp.K^4ԬcgVU"Jd.`AνH%AlcT>xօ} !Sd_Y2_fY@5l8"}~AW6Dgwsy`FW f]_ \&yq%#R'V}qYc^g&wgd^y iF& 'ۃEٜ;#o!,''H0RX#N9@$6ñǏӤ+Ҁ\re XѲ͎i sM%H| m УWP](QeMYajHLEy@Y(HI!bKW;9ȭd/W\޾tA028p0s/;x<fΜ ̙@F͹RƬc˖zسs%I@r- vI-\w)[={;/pB(uN?eخp>V@vW % rt iG]_!!"VHv &^ةhͩXq  r6i8GREX%nsxbMwLGW 1Gsm_V!,''=HP@ `XaA6Ǐ/@ .\rrhв͎ V@W  rFt iG]_8 "VHv &ةhͩXqșr6i8"GREX%nx"MLGWPAsm_V!,''HpĒ XYhV4d6±Ǐkt9j\r~Ѳ͎5r-K| m УsPlQeM&aKL]@fY(1IIʝ;WA-&a&\ p&NEC& 6y0,_ʕ!&BN">ͺuvM{!q&R5!I +Xb4X>XcMDhL(r2vKVpYKޯ{ ~esfמ5/zͬa1lM xYք gws|5 wW`i ]&)0q-['b@+^~ycn@h&x Wl _)iJ. WWۄU ?#r!,''H9ZX(C 2Ph6Ǐ_h@@8^\r!Ѳ͎/rqM<| m УqPiP Ma Kw7Xȭ zcoߺv 1c)v003`8 @0NiU|ޜūc˖z سs1C>n螭bvy@-\,x6͋<;b{f.lyqs֯ 'j | u&f>Fp7fyM (b^ Yxv) ͩXq;X;и}&Zi( Gk=8 n'H)6c00] at_wd~EZ9W@!,''HPA>dY8FcZPD6ñǏPc@iʀ\rHӲ͎e|lsI| m)У PPe4MHc4ILMGk@Y(@I#ʝ;2 ; 3hA/`U8̌/f 4LƁ\ƌ9-g 3ΘgLkת'~M33qM"&S53_ #AР4ol@7@csfH` \~#LLGY3F}Fֱg8@3n&}b@}]_fdaŠwev3HIHqx!񅊘 fنj } ^5YD#AHpZ~1&rTB\!,''Hp .]Ym uCd 6ıǏ)\r.Ic"tI@[ J"=:pI!JY&ZddE:jɯDRt)H %V)Ep5$DQrH`v+7-&NLh!2 \ƌ A`!Θ'ׯUmΊ0iX۰y>:&mh@v[/@1B˪!̊$,qA5 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/db-green.svg0000644000175000017500000001457400000000000034754 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/db-red.svg0000644000175000017500000001457400000000000034426 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/drag.png0000644000175000017500000000015400000000000034160 0ustar00jamespagejamespage00000000000000PNG  IHDR G3IDAT(c`())IMx5)]YI Ƀ$ɑBB*U3_IENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/favicon.ico0000644000175000017500000002367600000000000034674 0ustar00jamespagejamespage0000000000000000 %6 0%'(0` $CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC( @C??( @C././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-gray.0000644000175000017500000001422300000000000035305 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh5PŅ$-܉15[-p@ "[˹֎ ܭ;4 S}{,I㨸nW 7Tr"$Y84Ӭ v)5ݼHF5Ҥ8m&r{n\4P[ݙHE4F~X?||,2Ŕp|i.Hmff602_Ik1jzL2pFǜ$ts3R1yD_fgL-$C^l19Ȍ1ZZ)F?c^e.x e.x mB?Vn@YLPxJ{|d.)'q-hB!N)أ 9;I h")ޑ ctYgE`Ic<(D~$EiNKz۔K*UExʃy))p}[#f QсS,i f0lP0M`!IQT>1OcDhWkbUR T#S5()&'[45+zd/'bQ}"AH=6$G]0sD#Γh#$f(r[~41 a{ѪbI@%?b k;jzt- RPǰ-@ ^T?JC-xSt FKS();K .G!!p@6غ%,;Rנ(i[I 8`e*oHĜ EAf!KE)s`SHx\_F!S Ƨ/l0/\C]v>B piYDxm߳Jؠp{:vk"<ǩIqi$XeGqP&Q}Q: ǟ,m#\ߒMqny]P1 0@ u @p\Ҧ(X= `n넨'&6 @i5 3!nK 0$`p/?:Çt ?W}+PGѧuKXMs6Hxu`›'7| emIq!yCݖ :U>h|&qQD{ݥ8$_P7Ɂj5>x n9i;^]/ͧ`ߒ{2L;`nuHkG$K͏ga(nw_˰Aės/a) MgWLG&YNSq}TJE{k4xK>(WM؁1^0z ͆L a{pyv .|U6H*E3L58 qP`g%HM'P#2KlcMxKOȅ7xN YL[ZE׃~geJgH_PXlN_],88|/&|Xn^lԷzl(Ohy4Ci7Pl ^Y <9isq $vvÖIpyPǕSPqy`h)0K19JJSm*_ ∏: mVH㈪%G*DPz=juj㹨z0@:aLVp|* IZЬ*ɭU୰Jz2: Q 0pjrz`W:G|15 I 꺰Pp'0z  : V-_Z+`#Kk.u4)Z51˱ ,F܊ 'V)g , ' YPS  /0|«S 4Z( = &р# i i*2<S[ f $! b{0"X۷᳦񸥠kAaK p|qR&+'y $9 (N"`' N}: p[{b' '@ k $~pɫ)`p+π;)՛ W \ oQ‹ыm%)kk +`iz  $W$P{`P 0pP1k8! ;* 0J|;+N.9)lC p\, 0c(}bW {p0#tL PhL {, ħ0 0 ǃȐɒ<ɔ\ɖ|ɘɚV;././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-gray.0000644000175000017500000000601500000000000035305 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-green0000644000175000017500000000601500000000000035365 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/firewall-red.s0000644000175000017500000000601500000000000035300 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-gray.gi0000644000175000017500000001273400000000000035316 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh5ɫPŅ$- 15X[pϛ˹֎ ܭ;S} {I㨸 n W7Tfr"984Ӭ v)%ݼHF5Ҥ8&r{n\4P[ݙHC4F~yX?||,"hp|i.Hmff602_Ik1jz02pFl$ts3R1yD_fgL-$C^H19Ȍ1ZZ)?c^e.x e.x maB?Vn@YLPxJ{|d.)'q-hB!N)أ 9;I h")ޑ ctZ&yLRQDY K\>Er+_͒$.[V|ߠ/\FǜIQ+zle4PR. fjEm_^L9prτ&7@r3*L6F `& yJC.`PB*KAoε:64* z0D9!|@hOpN۝%8yjIq)\C'YQdmoL#wZ!ڻH?Ď-pzRH{ #|s mbq~ OOO2{pcz{_Is~V 6 $diLpI  -7z'2x7{fMg !gtkvau{y * '"{8<.h>U>!Uok ?Ru|Mr'hf }G#ą!PDr@LJgݠw5r5 ܦjHc03_umh)ćC3%h l{V.8 86j0n{8"[XgljGVQnXQH2w[h;8i_H=p|{V tB|79_`䵂bW'XN%Xxh&C0ͨg`e,H `V(y`D^АX' 6U  L}%iHdIMO@k-INБ.ِ@ip'Px w () -jU:9> DHPh&\b`` [S-u lY(w!L&zci-t B'( ]ƠrД{ &0e $`X h`09eQf dyIany ٛҠ񱛥}0᛿Țˑ`|R& v`@yzS ܰ 2Y ay $i 0pm`i% P Ay é) U @2 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-green.s0000644000175000017500000000472000000000000035313 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/flavor-red.svg0000644000175000017500000000471700000000000035330 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021100000000000011447 xustar0000000000000000115 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gra0000644000175000017500000001241400000000000035365 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh ,P0㺀$<|,Ik(þ!(JC $,`h 0XiRs zGAnumGnfY:$휥$8mk 7N>F0>0qEeh 6mz+L# AE2Ǥ uj*e}dٴ4F5Ϛ&l};n`OvBȳՙ'(! u~jK(1i(~d9Jx: t*Ⲝ"&z-nܴڊ˷릫p\zZʵox ڮ[?rK{( "Gpò6.Rnk߂  {m|8ƸdK:7;]ٖiψ Dl hfkL| v؜|w`Xx')2ZpɷqR8{{ď!-rq8s@ TdK sKWt9}`Q 0 z9C!Ā`ݠ{4zդEФ8 ?E L`V 4x DW8‡[ !Ұ%þm]Cc18 4!(GBC2X,>I3,p4t\"1RKd"PH-P /F$tb'0Ixdpr1#ab] D"AދBC(qeOh%ʘ3>CN@ABLQj;±.A9Zt5B "-SUFa0`N&,b.r `@,F;s6Q(0K6Tܘh,(;xC mZU2%0X GCAB>!&W ;Ԋ lu,Yi%l(bU~[RLͨ+j-p[Z /Q@sl)xkEWd7.-Ϋ!rW0zB.E7\ HJK>.M0_P]aáx.`B/kq(n 0l[4*㛡`raQ8xaɷ<,Ak@NPXͨ>gd?@>!~*lu:M賜y1+jY0N%ZdDŽm=4B= D~qbSg4ldsgY-%.9F\sv״9tm\7~ͦZB6;ܩ) ]kݟy]#an^}c:l 1٤vcK-LX d6lkaU#3VM@pKJ)G{txnׂ2ǵ"V]\ܢWt`y[F^;E @Sͺ[rDύt=i ֳgs=yKB.F ]y-~"D;;^b-u?#-;__5-Ggj((y,DSsy-| cMHRȗ+cr*Óya{}m}U~n.M\{~~}?>o?O GAR[\\`Le'l>`hBuS Hvdp Cu~ca% Rv@0fU#6_3)(IYD.Hzd:3%cnFR7u#=K|0ocЄQ?~ q GZѲ\%~ (1@W6l/0އgrp|mhesv6n7}{(HrHvths7HuIvx {0 8Ȇ3+ }c؊~``a307؋~^7ZCp3H`s{(}H 16e`H7x(E46`x6#iÀB .؍/Ҏ=sg"0 , #S 0ЏzGF ,e "2I hQ+' ,h$ y A)PZjBo%- THa$I L ʈаqh!Sy٦ ?1sX #eY=P7b}ePI Pi(–0Nї 1rhq҄i Pو"Еj.9 0bp0zqr3paiP2y^I/P)I Й2~ `$@ 2a.C$AP П$!A 3B 48Y[oЩǓ8iYŠYC0a-!GRq#Zς0:<ڣ>@B:c;././@PaxHeader0000000000000000000000000000021100000000000011447 xustar0000000000000000115 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gra0000644000175000017500000000415100000000000035364 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-gre0000644000175000017500000000415100000000000035370 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/floatingip-red0000644000175000017500000000415100000000000035365 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-gray.gif0000644000175000017500000001343000000000000035247 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh5PŅ$- 15 X[pM ˹֎ ܭ; S}{I㨸 nW7Tr"$I84Ӭ v)!DݼHF5Ҥ8m&r{n\4P[ݙHE4F~X?||,*Řp|i.Hmff602_Ik1jz82pFǜ$ts3R1yD_fgL-$C^d19Ȍ1ZZ)?c^e.x e.x mB?Vn@YLPxJ{|d.)'q-hB!N)Hأ 9;I h")ޑ ct) t {A AB1,(A@TB!',*c @ \!Rt3 - 6"ORsj-kb]{u0kaؿ$A-v˺X@@`Ò? KtA,x,ᒡ& $LBYb͖kAҘl `,:&!u|;g8ƶjw!} )\|%V{.>ϸR93p8ƬD;T"(Ҙ>!Ļ_W+\M 2z&F9pzwl]p.X< "{bP(~0=PB@(>  8Dj<wpD<`Xp.(DȾbM]H(;2^Ќ}F?ύ$wbIz#,aS0yA-!7!"1ۃ y*z8.<a Y+/{c*I<Z8Kn`_xr1; a%+ȑyb4_XD;ieMVoB.umݬCy/0IR 鴃19H=T‹y8iW@CDg@F7)F@*C>⡢<.p5|qvʀ<5Kѽz"m%uL75ٝ HNW]-JLZANIɚҡV >!|Bwpc[[*Ar{kτx*)X؄i]H\|EM@Za3r)ue_l۪ÃMA#`zh6OwŸc)oc:8nr\="HP2)=[gr\MӨX<8JA]#u\[dފզ8X8sl/!¶-l3oWdS' +/g2Ywkw r6 %Po:.b@o#ãAO:*k'_05jʃGwN]\ uOkfpܦ\pe>q!b%D܆$ę 3vn.{vSr>綽p&8kWOQV #/c[uy TH(CnW^S,iূ$Od7DMf) ]]^DQ{xs8h wW@'\ i:(ܦ@qi0q!=H]îMo1?zH:kuQ,̸=4}lo݇C yS6FƟTn0sGaosW}7Oz{Y|!ǀp^rp eLy5|rL h[qZz'8m7%qU[g6~$8y : 9| &7nb]0_jdDŽjw^`Kvg`dqrPC(ch ޶vy~XwQtx 1XHd f(K8|_\ַw'>HC(E7wQȁshE5 Ek%XV83,}{7ghY TH{vi`iPbQB{._~@9Qytd2 ug  Ŋd\ /DB%Вu- @CC%$9FY1_>( )r`X3H#{u@YA3773ph @2 q2Dt40p0e}o)  p#0c0gmsfxxh lP vәI &Qg)U`i9 MF]y9?t6i 9 k5Іۙi㉄l ,( MwBIX9H  0-V0iz靲4ؠCQ0p'y ; I }-'Ze  顾pٞs1\:J 5-ڜȀ5AK'bw0-񩝊Q 8j#RК\"" 4 e !0*@!x$Y9P! hࣷq> _:؁pQ*"Z jZ JћZ . := e7'\x }8 (৺APN ' V9 0, #P/ '`Nj $`pͪ)\ c# 2 oQZ |P': :P`b@  I $X$욚  0@`p:j8aS*Q N{?1zzZiвZ+0U VMjW IJl$T m0T#@x+h5P 0෇;[{۹5;././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-gray.svg0000644000175000017500000000416300000000000035304 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-green.sv0000644000175000017500000000416300000000000035273 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/image-red.svg0000644000175000017500000000416300000000000035114 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-gray.g0000644000175000017500000001605200000000000035315 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqhj>}EfvD6 0BI {AVjɊaB %(OusVA:zEchPm(*DbH yA: ɐAZ* di>#y¥>yCZ@ZPPyNh>i+r=l,C1N*t; _̙ ѹ 4@=P'>d L0 տGp(9PAT0$3=“2̌)<*l9^" ZA2L )DA ,@@Ml#A|16 Qn",H3Krr>#T {_q9B93\%(p= }YO Gpnɷh `> ސK $P|Nx9W:-gp(o,E? 8gCT)(pЯY%<` 엒9-`1ub^!.g C8Ϙ?%.HTXx纁9ZU  t>ƊuP89 4d }h9PATehH2 014%S Y:qYPA+U<"Yar\`_9e K#Gfq# 8Xh }`Nc"aQGA.0x0.9&k882-|AKVؐ9lVh pu.\I^r>: %nB7qNA[ vhNCQ na>)5ւ\'Ε1My]‰RxH^d%(n6dc-J"6N'@9gڂ@Qd| CH%q< *ѐyf4(N:u=gMr,pS'^e\DdII4e9zYCss$EQEʐ_㬯$-G8NlIٺr+]:6>nIfbGJr5%Gy 0oӭARD8בRrP&2K,^V+lq+ vh0X"H%A&PfQ/ti+F|)w/! Gl9[cze0j$D q,[H+#"Qd >`5!&gb׬ljY 14"սUPdPZU!n\|@US8P>[(!!jh(!}y|=qx7g kwwucO@{EF8| t n#avQv'8`k-qD>Ղ{RUm}!z<(Z1(AH@:pGARWphw,Az<7_Z8dA@ZAQ Łi(>^X`jpԆnf[*eUuH!VHǃyHnCH(QBXu3ht7~k}hZH 8.g,qo8NAjAt}Wlh%HsփWuZAy7{ѷ}PoW`w Pz%(z -@t؂SW(H{Hb H&i<ؐASX4dBQ04XhWC*a-8(x9ur3gW3 %U oNJ,1+vSQ)9 ᷒hjZ)'-qIAfRVPwe1ip+>spq(H K_^ t1lK  z Z0_+2AD;pus@K w{B  N}S]M0;-Բpr{ {3!p*vk`qDQ  ; 2 'z۴.jS+n ٷ0 9(a#h<k-Z `b KER ԙCI {{~2+ ׊A 倄|@z KAcм:U\ E)r [J <NA7\ k* [ 2|| x gŠF ?< QL˜ J9âpC @¹BLw0) V; {vNB7 ǚR U[ k9fĭ E @hj[õP0B-y,>2>pwŝ` <x(P`| _|`-29 ,uƎی idL !+ I5< Hِ<ϥ _5 LSЕpq+ a Ⰷ@,ѕpP-҉Ј1ѹ*]ټ_\)1,-0Iyox& U>=r:],urc]Ԗ% ΄\!d ],,k14$; %e"T]*0րrj_#ͣ%V! 1)،m 7\1AR*A ـA Z}\(m-m)EQ^}C<- qK ( 0Q /0'Aڲ}'(q`؀E}Q ]fܽB6M0=ޢ@~5P %(}ݧ 0 `,mx1~ ;  ';z T-啰U>X[.c>f. mn 0p.s s @Qe.Nn $~阞难>;././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-gray.s0000644000175000017500000000600100000000000035322 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-green.0000644000175000017500000000600100000000000035275 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/keypair-red.sv0000644000175000017500000000600100000000000035320 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-gray.gif0000644000175000017500000001636300000000000034572 0ustar00jamespagejamespage00000000000000GIF89a\\ﻼޱҩ㡣踸͹㮯ڸ¾񳳳ѿ백֞޺嵶ⷷ! NETSCAPE2.0! XMP DataXMPB?xpacke30AFDCB" xmpMM:InstanceID="xmp.iid:0F7670F9E3cket end="r"?>!2,\\H*\ȰÇ#JH✋/*ȱBn5CIPqѣˈ v$t͒H'B7IjBhA3&%A2AXQȚdZ)TTdh]*N 0,D7gsm˗@'P$˸o$g0 CT$33~ U|%Ie|$1.ePAMo Z׊Ʃ;+o`8.w/NVs^(2 *NGZbv}N?FR TPt fIT 6BǐVa|2E}Xas$MWha\vЁxx6 6~ b i bB 4 j'BnEH-~nlY@biG"aAR<|gl8em<袌6裐vMVRp1@馛1Am!do&zWYfǒꭜPWsJ밚@۠}j)묣x+j춋ށZ& !j pk.j&d!U#[ХԽkK٫o_l \IZ;ngkǡ9|S lI$*oIxlf"DUGX|̑Tvt:IIkZ$+4[ r \\{}ZZ+@˝) VӢݪf\yj1ʞM۸g;!,#2J*\pa Bŋ3jtb CBRD\IQIWXYc8A%OR LъX8t×PJ5իXjʵ'UK زh~ سcH Vt^ Xb dD@† kbă>+_+A˝)•VӢݪf\y'2ʞM۸g!,#28@C*\p f@ŋ3j C D\I#X8A:8!O  ъ!r8×PJ5իXjʵUKزh~ سc; Vt^ Xb) dkA@† kbă>+D*@˝)VӢݪf\yʞM۸gW!,#2 F*\pa  QCŋ3jLb C @D\IqIHXc8AF O 4ъln9T×PJ5FիXjʵ4UKVزh~ سc І Vt^#Xbe d+B@† kbă>+_:@˝)uÕVӢݪf\y4YʞM۸gg !,#2G*\p Aŋ3jT C!"Sh$+cч̛ @$'mZ@Dx販ӧ&_xJիXjB֯`z*Y^ϪJnԨ6\:Ni=xp}j㭉] N;:Z#^Gװc˞M6l!,#28 ņ*\pa  n@ŋ3j|" CT*D\I1Xc8A2 O* ,ъ&8ʔ×PJ5 իXjʵUUK6زh~ سcQ Vt^E!Xbw dA† s51VW!{eܕ' תe{-VӡݪJykʞM۸g!,#28 *\pa :@ŋ3jd" C4@"S(+ct1̛ @'FZq Dfx販ӧ&+|JիXjZA֯`~*Y^ϪJZ6\>+i?xYn5\pU|g;@ ܝJYss:Ӊ%F]ABװc˞M6l !,#28*\p! >Aŋ3j Cta@.D\I0X#8A O. Dъ)*84×PJ5իXjʵUUKزh~ سcg Vt^!Xbw dBpA s51⁄U̕, -תe)wVӡݪxk-ʞM۸g!,#2@*\p p@ŋ3jb CT@"S$+c$Q̛ @4'FZ@D:x販ӧ&,JիXj*@֯`.*Y^ϪJ,T} uMU@~7kۯ"pS#[ƬYAcU=^-@װc˞M6l! ,#2 *\ȐCHHŋ Aŋ3j Cta@"S+c|Б̛ @L'F_Z1@Dx販ӧ&JիXj@֯`þ*Y^ϪJ6\3Ni_-Xup}.<0bP]Nod2:Z^=Eװc˞M6!,#28 *\pa :@ŋ3jd" C4&D\IQ.Xc8A( O& , ъ+n84×PJ5YիXjʵ UKزh~ سc}bVt^!Xb dA@† kbă>+j@˝)ÕVӢݪf\y ʞM۸gK!,#28 ņ*\pa  n@ŋ3j|" CT@"S +c1̛ @'F3ZDrx販ӧ&dJիXj@֯`f*Y^ϪJ,6\(Ni3xp}j㭉]N%:Z#^@װc˞M6l!,#2G*\p Aŋ3jT C!@9D\IцI4X8A>H O9 4`ъ58ʔ×PJ5ƒիXjʵUKزh~ سcu$pVt^!Xbw dA0† s51VW!{eܕ/= Mתe{VӡݪJy8ʞM۸g!,#2 F*\pa  QCŋ3jLb C @ "S$$+c1̛ @d'F Z-Dy販ӧ&ӈJիXjF֯`Ê*Y^ϪJ,ڰ6\piEx0Yn5\pU|g `ܝJYsn:Ӊ%F&Kװc˞M6,!,#28@C*\p f@ŋ3j C D\I#X8A:8!O  ъ!r8×PJ5իXjʵUKزh~ سc; Vt^ Xb) dkA@† kbă>+D*@˝)VӢݪf\yʞM۸gW!,#2J*\pa Bŋ3jtb CBRD\IQIWXYc8A%OR LъX8t×PJ5իXjʵ'UK زh~ سcH Vt^"Xb dD@† kbă>+_(A˝)•VӢݪf\y'2ʞM۸g!,#2` *\p rCŋ3j, CàcD\IыEXɡ㖙8An%Oc ъ r94×PJ5)իXjʵU1UK زh~ سc@ Vt^5 Xb dE@† kbă>+ @˝) VӢݪf\yj1ʞM۸g;!,#2I*\p! 0Bŋ3j CT@JD\IqI X#8A.#OJ 0ъ@9×PJ5CիXjʵUKVزh~ سcp Vt^m"Xb d+D@† kbă>+_H@˝)M•VӢݪf\yDʞM۸g !,#2 *\p! Cŋ3j4b C3@<"S$ +cё̛ c@L'F<Z@Dy販ӧ&ؙJիXjD֯`*Y^ϪJld6\ukix0Yn5\pU|g zܝJYsM:Ӊ%FdNװc˞M69;././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-gray.svg0000644000175000017500000000465700000000000034627 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-green.svg0000644000175000017500000000466000000000000034757 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/lb-red.svg0000644000175000017500000000466000000000000034431 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/logo-splash.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/logo-splash.sv0000644000175000017500000001344700000000000035350 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/logo.svg0000644000175000017500000001312600000000000034221 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-gray.g0000644000175000017500000001431100000000000035336 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh(I"BxnIfT`#E҃ hB"Q2"v!FB(.)VbA#&a`D 8Ikp>j>}EfvD6 0BI {AVjɊIB %(OusVA:zEchPm(*DbHJeA: ɐAZ*di>#y¥>ybCZ@ZPPyNh>i+r=l,C1N*L٤,̙ :,{M벫x,#9ޒ03g<[k.3!/*óbp9 rǷ[εD|$Y-/.7| KK 3,9R ZK"g `Xg\wt+cC )| (nývsMwˍ|b#6V0CK 8GĬXxCL>,i0yGTd<ԛ7zВ,~ rdy:;p ʲÎ4;/`nS_7DB*C/F{c<<90~>OzR#F5|hxrżn@`#8F'.Ђpa:QlcI5p?V%_%`-~-ta9 #Y*abFJf6Rԝ3(<vV9 ҟQfWdvFšK==Ћ S(=k ܍C蕉N  L6͡ct%hyI`/1&EOFsuA o҆xxdE"`A.Fz!  V5c=kZJV!0ZV" AV) @e9f:$jmfZ$6€)9*  \>^J!͚M .ȴ0@gC>)lɁZiPc03{,TK[jn{نhMD>ݎ7,w!2>Ȼ0\ޣBo#"rfK KDv!yN6['`BX_~QZ@AFL(NWb!xgLX 4M7lڂ9(ƒ}8a9he /;9idq-ͅbL8WW4$rV.;rPoV,o19YuHs9|NA aEMZp29:LҠG̛jsO_yvm,\&r9h#HƷZkxm`@ Α2"y3<7#*|9Oz͏G_`KO{ӿ $X0`Jp 0 <].?gx-dAWϧQȗmE`[˥V*0>QqD0~nT~J@eP  G eYQE1|0nT[}DQ$pQ>ć ,D. {W O5 (8* 7B9 a>x=( AX4`g$脥4Pta= OQ(X0&tp;W {}'1ZH5x v@`h  ufh x?X Y r8ik1m;P2AЈ ]eZ'H%1!@c0t`(qDrA[gH~X H= 0xz x؅H:~ h ш< 5v H <8%8e8qP0u؍/lp(! X w`@x8DM u%)'ق))DjވdᑹP7 (W8:>y h؂8 H;  XDc&)2Y<8 @9BiDI F% e?ٔ= iɔ~8h02PJTŌt k)m٘o = ow99Q@D56ؙ#Е*d9aחY y yxLМp~x p}ț sG8 !F:}yDۃ@3P0Zz :<px1Zj3Y AP`6F`X'Jڶ&%ڢZ v1j8)Z9z0ʢ1Y:4 >餐V< ItI'b sg FI3ӦMZ o7Iɧs ٞ*LZ Ȧ ~ 04[Pzgr〝w J àfw/ J'7 s-㰔pJqJ *˪5 ~Z.azR4р a@ht&8sbzj:42L)|گ m8qN2N~0Q1:A2v,*q h/PJ+/v(qQ%Jyҭ"ѲE"{:2s2X{# [Z䀬5T"/[% `+!H{#,RiFv(nq  ͲD[y{*(|{-Uk)Q^g[C 'j#q-p+ 1qǐ{gи!H` `fp|z+#`#p(1;4 p3A0 0 pPjxԳj |@:kj!| l0;M[! j-< 1;çd_8 sF ïT3,lp$"EXZ\^`b ././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-green.0000644000175000017500000000436500000000000035335 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/network-red.sv0000644000175000017500000000436500000000000035360 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-gray.gi0000644000175000017500000001460400000000000035322 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqhj>}EfvD6 0BI {AVjɊ9B%(OusVA:zEchPm(*DbH UA: ɐAZ*di>#y¥>yBZ@ZPPyNh>i+r=l,C1N*L٤,̙ :,{M벫x,#9ޒ03g<[k.3!/*ó҂bp9 rǷ[εD|$Y-/.7|KK3,9R.Z%,G.(-Ұx0-pV\3E4td5>:+e-,K8mIs31+݀w xkJ~kBx9>Y&NPTKA*o,;0O{+)/mW lTX) <]Ň򾟳)kb5>.tϨL#(c>Kԓt_=b8dI`W@Y揂(6d)pd;`+L V ,jmo0?0~p9aÆp|:aw=dE>ԉW<"RBPEY;@HFUgأQH  $- H6aLV⎰Y#aE#fQ  '~ qDG,], Bx@ p4JT g49,LiBeG7ٶFԲ %Jp c9I| "uV21Lg3`G!Y~nb0% _,'>yCh9z($8xx,8wNT/0`r͇D3A&<ރf.=HK qP Ej "%`]6J,4dbQUJ _IYWS\nC,dY&b`,% *0MRi%p+2Zu`8![sVmw[棍]@ iVΎ6 GPp^weqW}\WrERvx@8,_RX$,p3 㾎Jf]vm9Z>#`T^s4JlFueJSt \E ǮEˡF$qU#H"k aE @;/i oLQ BurGЌ8V1n C8>z)G6`qĵm^5Ay&J_Dcp)'gB<${U79b$&O 9A\B4`1yGO">}u&q@%Aw^g7GE3>ыM`򯏈I"oΙ?8=YUo::+gw· xlwpGpmV{2ŀ~0ndG Exx Qr7k&VwT wzR╁0 Z!Z0x`Yף|tg4d`G LGy lj7O6J1G>Vh 77UVVP}臆kgpfUvWe,6v?xoRU8lxj!煈1ʥkW7T凐 p8C&R`|ȅxD&0m[Є 7v[{8P0}n ,$y(9{ t8pHrGeiUo`X5਎L䀎0@XhE8 V Tv5eSE%J$(؊Ul,t@IDI}D rXVvxH=0+@00v+}l(z>^gs}t 8)AI%5PTy #05T7Iy:` D샕Oifh H9oVI p\{i@wI3 vdixCKSj0Ii3⠓ KmH i֒9wpfiP/I uٚS3q 36Qi c`*E f#ٔ雫М y60 Hhى)y}YyxsГhFٛY:鞳˩ lP}ɜ)`QЌp0  x ɍ jINٖa#zw'zvW!F-j䐛 9Firʟ7* h.9 2wFX%$(6 )1`XФxKJ$ꥡYWJ /gj,ڣe[Z'Unp| bi簦U :&qp: ! y62 e0z> ڎ y,ɥ]jr@P{ʬ: a8C: YZs94ZTȭJv Z :j T8@7PЦLeTBD wwS>@  *Zz J걒p t& ( jٲ @* 5бy5`rJ { H zO〳 q+ʵp B;xm%xbX^uB {gz0ZQDZz%f˭Ác=[A2v3,+Z"7$Qd %1."(< #尺j"qE"~+{"KD#["[>k ;:;9ʫ$h+y,A!p.k={(м:K1S ! (`! 90'""(bZ@@zܵ2< :*L =p:C+` 0 ! &cLM[6 "<[LP+ɉ•lƘ@qFɖƢ, ɭ, Z:FRsɺ< 15P |\|؜ڼ;././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-gray.sv0000644000175000017500000000472300000000000035354 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-green.s0000644000175000017500000000472300000000000035324 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/policy-red.svg0000644000175000017500000000472300000000000035333 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-gray.gif0000644000175000017500000001176600000000000035163 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqhb(# e-8{u1ȹ.=]&(a! tJXZ2dWӖW_q]D\0Z" ri|[Vtt%+ &uu1 p:N! 1؎W,;@t|6A1zMg,^\Th1*-+628rnZ  ;>pQ JZ߾Y 7Ji%UCJG=Bxg(Y)%3eq&!>$J%1A"'^LYD|6]%Ȅ0b$PB:A($>^ %(RFQc%`eTVe^~ f!I(w(pT,XHpY'&xQS9Əwj`@Ul9H\< W2Z"O8*H"^ȉ(Sx*8B*2&-ꡘf9+=b1U,iIS&+b⫲j+vڬb^鴁2+ųVNf"n2ɫvZ/\{D=',DH(,8 !l!H D 26ai,׬r"6qD| . tQT;;9HF,mu:V|BqEA0;`Bv`j wܻ]&MہrI4߄% [(@n;>)zY.藋IKf)~鲣.њгv B>; \C<0.7s?ΟJJOWHH1>nÙAѦ?w9@z7A/t `48<.iځ +/ !8l`g2,^0YB>5JDpH,cXET<"D NcX;D1Ѝ8!!X?KЯ|b~.Hxl A}l>2)(xҲAяD_IXbkjD(8Mє\I^$%#/H+6ayJ^~O#*&23*a 3& Q5$E%Z0gp}e7 MuJMx` qp'y= ڣ`>c:'PvZЊr"b-YQ%PE;:P. ϑ%(9IRq"( Ζ 7s.=g%t*P Jf bZN ͞:n] 2Ы-,E:.h30gSɺ.ht+eUkֶZ)K몴.m)_օeb;:Xuj!֬ Z84ZVh[bY~vem 7r7aPKu,T-QP\n/JWrn\D, nZW ؅%J{OM]j^֎#{K.@T'K_M_qseA\`zruk.(Q*xk'LmAVp0-K.H,ƅ%.p=O=q`@77@[L"4]26p" a]4햇Q@Z3Va$BxTڴ0HK KU.-T (K]B9 ̮ Y^GZ Z}fG,(W;,9%o?Mj"XI%R]VLǤ4 2a[suЀ oV LcSg8 =R71kSmnA88umN(naj7ZN~ ܪ(=n 0 sr+  [;'3 ol\$hA/Z?rѝxѺ ;m 1852|~' Џ~quB wk ~_}2@2AP/pGڔqsHr7UW 5fڷ_`{wyp\x!#8]mW*ufG:„BR duB)hx0y%bЈ"z [hp&@Daf! ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-green.svg0000644000175000017500000000424000000000000035340 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/port-red.svg0000644000175000017500000000424000000000000035012 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-gray.gi0000644000175000017500000001630700000000000035345 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh5 PŅ$- 15%[=p- $ϛ˹֎ ܭ;4 S}{LI㨸LnWA7T&r"%Y84Ӭ$ v)A$ݼHF5Ҥ8&r{n\4P[ݙHE4F~yX?||,6Řp|i.Hmff602_Ik1jzP2pǜ$ts3R1yD_fgL-$C^x19Ȍ1ZZF|8H`e.xǵI0OŨPS,ޠl$Kj'py/FR,zH!d.D A1*cd".*ǎ`!*G1yw+| 0@ , YZ~$p  P1-p^#@8,At/) j4KB"l))n PCRB, 27Tlx|ι$c.a..:#rφ\ӆ0`DӅ@bb:UC{-:l.#s $0VxHK1U8u717Ms^2/78j "4;wGsS!n֊x 42|b54ES X_4:B)̫|=Cp10T} -Xom" MƚBFcWJn(1>,Lʢ"3]@Y#K,'$ .'~ye)XaFa -rنvh{P)9+Cނ_WߥM\<0 f<8E!S|Q@O-(Q8ORY r!p!azȴ(h¥o!j4 iN3q$bx,{a7OOA1[Vϔb 7. 3#1#m=hlӉ>kV@@\:Op)^cazT*$N ;9'ڄBPL #`M{6ttJS`m۵ "OeRR<05Of#%}ȝ Ea @8ڍx)qo+y 󹫧3\tI(l Lr鸐7`՝duvBTK[\ 8մj(*!rhӋY: > EFڽ9VKSԫX Lu̼ Q_0'|b%{m4Y`դ?Qxcahm'“⦉ 󃑵bXN~bn-"Gg ٴZ#'z/7itFV:cAw{plcbv t`mPwBJh dfi7NVnZx dI`? c$Is;@y(p! EK@jtrlc7oIMW AU_Ex H(N3'A׃>(TZ`L N@prCЅ:s#)6n.8 &Fhi?tX &A[u nV J_gsccC%x u72b]R#xe x)sp"H>P#łYpQS}(TpGZU3[v jH HtȄWUFb%_v GwHZ34ChȁlF˸2Pă4bFq܇ 99p v>4;  U&#)wƉ@JRtj 6r6ғғPF,b}w"ypgs`m+E@‘>p!@#u8/l%Pe@FK A¬uc@D؁P{rʷ s\v˥J:`A][5L ~ÕC oĝ| ~c-Y)S-6}̩ m0n6073Ku}JvΛ4x쬺`t0el4~ÔJAR~0;(Ƞ)tL * r# [ {33|)wP?.mڼC= 5,3/28݀esI Q|m}҇5g}w@ɤͤL, OzL,Xց 7= {؋QӅp֣PҎ;jآ Ǖـ0ʃhـ-bL0lؤU=J=س+ //y)ۇ[p٠  6 tMM9 ~R ۢm-m4}ٝq ܩ W "]ݱ٣׍ M&~ \ܫ !ڶQ06 E(- W(B,M q  !Ԡ*ѡѶZ"уqᥐ@RP! }MPnn iRN }O^ '^ Y^ˁ׉ . J> gD]^ [~$z{ ; @)<r~ "$un pMn%P aŇm1 ' Gm %@p*p.π* Y BPŠ鄲\"H0Pb@ ž %X%P `P 0`m8>  @P 'P^(/@HN\ (o ,U>#(_ L#]/$p>Ph2Eo !P@@@MZ\^`b?d_e;././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-gray.sv0000644000175000017500000000606200000000000035373 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-green.s0000644000175000017500000000606200000000000035343 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/router-red.svg0000644000175000017500000000606200000000000035352 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/safari-pinned-tab.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/safari-pinned-0000644000175000017500000000262200000000000035257 0ustar00jamespagejamespage00000000000000 Created by potrace 1.11, written by Peter Selinger 2001-2013 ././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-0000644000175000017500000001221200000000000035457 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh445@G 6Fn @&Ue~ aM` TD€4C+PHt2$ o W64K vOowNg[;X*"T7~7|+ v à )|Y ws5Zˣ\ 5P."t4ٳv zBGJ4 ͛Ѝ&.sѬC)n!Ҏg"sјXD{0'<;9.(:i4N!=OMҥHbls7jȼ'6Sk) hِv47#(@}rڂLox,U79?'1.}!꧓^2`A+64˒Kb;رo4,js!+tޅ~UD һo!(- <V[} ï)&G@RpN?8-`ԅ8`%-0}z `=wu3H-$M iS6 it 8^vC=,~W@a >L Od!g]t?vՌ}H\ ڪWF\o G %x?MR>1RŲXH͍B`F6A$i$<͒lĥJA;(xX(|FR#ոʬtQKQ\  d7$"0e2-!zL~%)ܚ[z,jZʖT+;j>hnTuu!.ha˭쁀LdMF,Κ lS\ NvE8`J/."hg|KNrMPh8nQPL+ʓڒC0wǵo/,">Fn-)J"=\z.E,dӆu9J6@OQp1]/'@d \O|#e}}4O*(/_b-{{cZ.Xf'?= hH# @C;y]+G:6~ߐnoRl 2/V %3x~q uzgn}|zw7Gws87~~T ScWw(|x}`|rwl`c68W 'xSWGPs@ZÄPS THW G[h4 B X <BGyR @fZ["@`Ep Pxl؀c ~:hB.%axq9p~( {x yp؉]8z@dPms-@8 I|.Pw ix(( P|S @lo EwpuPҋ`rorp" 4xyҊHJ"s둋H#[&# Xg %mIro qU ݰ "V y %i pe؍X` c,xѰ$0p3#0 9A>)ϐ-(( A0 q!T((#) P@0b@ * uT^ X9, 0 Px9AjlC+A p `K 9N|{D0QH`~H ۰Ez҈S j@C Ij4 @9𙧠`9Yy虞깞ٞX;././@PaxHeader0000000000000000000000000000021400000000000011452 xustar0000000000000000118 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-0000644000175000017500000000402200000000000035457 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000119 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-0000644000175000017500000000402200000000000035457 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/securitygroup-0000644000175000017500000000402200000000000035457 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-gray.gi0000644000175000017500000001435400000000000035333 0ustar00jamespagejamespage00000000000000GIF89a\\귷ֻשľȺٯ쾾ǮΝĢι鰲—ڳعǂ୭! NETSCAPE2.0! XMP DataXMPB?xpacke86D0E6B" xmpMM:InstanceID="xmp:xmpmeta> !2,\\ **hjexjqyxxhtjjyv/Ȧxym 'ddzvmt̕qz qtx/ hLHEStXǰ6Ԝ E6 32| ,|#gƓN1E4@IhuZ ɚ@׉ĥS*m@-gJ6h ^)E׮z)J ֳpERe۵A:V÷2q(X0@Raa!SӸ;2gzJts妤&:Fٴxe8:ٱEc7ً]YxՉ!3bE'JdX+EQl"W~ P ~HE.8ƏG=((X AiFHrD(Vhn)Pf\(#P 'Ee,W$(f|+JncoԔ_!$҃?&)b( 7TVȆ(e"G`NJ-H2#lp)lFpR4xR|矀*~Q06-1 Ɏ);\P馚>hL*HFi4je*(b&]cg|Jk;ZeFfdʰ ^s9Y;64V[ٌTƲ I.J*k\X'[Fmq X%R4P뽵0͆eŸe&'\+1)xH}$ls[{f@-d$!w_\%pt@}Vw$Yr\O\bgM@nhHJy6JskdlpWJxy3Trv#L8;e&bkݓVaqWƉ[yY \yi&IݣZYE6VU/AkuxKyFP e$M9O$)J,N(dhG|!!$>_"僆4ubN@(lGZHk7apxoq\QsBD,fq\"(UX Q!,$2Z]I'\_Z`\'U`ZU]\IÖ]I VW[WWIVVYW!5~ 3O\o 36\(AX)H!ǎX`ˎ߮e%ěbC0]Ϧ>:5iV^eǵׯ`Êj!!,$2TQMCPNTJPCIJTIQPMĖQM*K)HEEERHKLLKH @-5 "LH=r 5!p)MǰɈ?>zBRI/?+"sr(%F0sgh@KDkAu ԥbݚ՝ׯ`ÊK@!,$2 = )?6 7?)7 =? Ė= ;<.<< ; ; @-5 "LNG=r 5!pǰɈ?>BRI/?,GK89F384`Ee˸#5N6}:UsLjꮫׯ`ÊA !,$2-42-!43!-34Ė'+,&5&&0,+11+, @-5 "LE=r 5!pǰɈ?>ARI/?3CK89F 384`Eeƒ5N6}:UsLjꮫׯ`Êu@ !,$2 ! Ė ### @-5 "LnB=r 5!pǰɈ?>@RI/?lCK89F!C 384` Ee˨a5N6}:UsLjꮫׯ`ÊUB !,$2     Ö    YW!5~0O\o 36\(ۀA)H!ǎX`ˎߤ :MaXE}7P.&8@'ӞKFҪXʵׯ`v!,$2)%) * )*$%Ė$%' ( '))' @-5 "LnA=r 5!pEǰɈ?>RARI/?BK89FYĉ384`Ee˨5N6}:UsLjꮫׯ`ÊD !,$2)$D%$%)Ö)B$%)%BB%YW!5~;1O\o 36\([)A)H!ǎX`ˎ `%ěD`@ 0],Ϧ>:5iV^eǵׯ`Ê$!,$2Ӏd$A$Ad$Ĥ$Accc߇3PAH h>}!3HŌ jDaEx J3Y 7aɳ@ JP2!,$2)$D%$%)Ö$)B$%)%BB%/|ՅZj"w8jtp"R[ȱ$Ď dA ˈ87B+8`Q!EcXA5N6}:URsLjڮׯ`Ê5H !,$2)%) * )*%Ö%' ( '))' YW!5~[0O\o 36\(A)H!ǎTX`ˎ."Qa%ě¬` 0]TPϦ>:5iV^eǵׯ`ÊJ"!,$2     Ė    @-5 "LB=r 5!pǰɈ?>@RI/?6Asr(0sgh@D1kAu ԥbݚ՝ׯ`ÊK@!,$2 ! Ö ###YW!5~0O\o 36\(A")H!ǎ>X`ˎF ڰManPaE}7P.j@'ӞKFҪXʵׯ`v!,$2-42-!43!-34Ė'+,&5&&0,+11+, @-5 "LE=r 5!pǰɈ?>ARI/?3CK89F 384`Eeƒ5N6}:UsLjꮫׯ`Êu@ !,$2 = )?6 7?)7 =? Ė= ;<.<< ; ; @-5 "LNG=r 5!pǰɈ?>BRI/?,GK89F384`Ee˸#5N6}:UsLjꮫׯ`ÊA !,$2TQMCPNTJPCIJTIQPMĖQM*K)HEEERHKLLKH @-5 "LH=r 5!p)MǰɈ?>zBRI/?+2HK89Fy#384`%Eeˈ5N6}:UsLjꮫׯ`Ê@ !,$2Z]I'\_Z`\'U`ZU]\bIĖ]bI VW[WWIVV @-5 "L.L=r 5!paǰɈ?>BRI/?"Bsr(3ˋ a0sgh@DJkAu ԥbݚ՝ׯ`ÊK@!,$2nPplrnflofnoPlpÖPpmCe ; hemiimeYW!5~3O\o 36\(ېAg)H!ǎԈX`ˎf%ě\h0],Ϧ>:5iV^eǵׯ`Êz!;././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-gray.sv0000644000175000017500000000541000000000000035355 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-green.s0000644000175000017500000000543400000000000035333 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/server-red.svg0000644000175000017500000000541000000000000035334 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/spinner.gif0000644000175000017500000000302300000000000034700 0ustar00jamespagejamespage00000000000000GIF89atvtTVT  윚Ԕljl<><,*,|z|\^\\Z\DBDlnldfdLJLDFD$&$ $"$<:<464TRT,.,dbd|~|424LNL! NETSCAPE2.0! ,p8ȤpU(gHj#.BGDN2fAbJN@!oP#'B # "sr3M  7H'B (31I r5\CxI(rD7I>r B5&&ԉ̭PA! ,@p(d0Ȥ`zl@r \*Cuhqh(BN} C'$*$'&&/p/S/DGB" P&pUC!^b"p(D'! ,S,P+p+ (|J#b *, PA! ,@pXD r9*F]N#C8+K,!(hR]Ne&%%xNOwMhyQ|4vJ'D7D$4KNd *T6C" 0N0 7C*$ 6B T#%%iKA! ,@p8LȤpb%Dı8ZdJՀ 9eh%nK'CxMQxn , yq|h~*'prn"wkmnJ):P 8H/q*H %`DzY7B8z/D zG:qY9B / Lq.OP$q%D ::'PA! ,@p8H1O)XB!;`*ph} `=M.T*z a bV|~zwOIzC~I $yBa&B ^B)VGB$'D)V$B #z WC )& SA! ,@p(X,R )8 pA g!5О3X5˰:-1<rwz !O!,N* .B R C'5])sOKB,22B)J!'BNCNp~KA! ,p(HlR -P&I Ð|Bg1>ɖ$@k[=pD [ C'RK 3IK+$+[8B808Z\D 6 +~C+6DB ;6t8dˬA! ,p(dh4 r &cH&bq Sle!2,\\H*\ȰÇ#JHb/vȱ,)@DɒPڤe :IfIDA$paL$AJJ"\3'qիXxh$D)v2jD?ai6o _K&oK2μ-ۼ:VfLZfqylUÓDtl "/{OpIWg"$d,'-,RAebA$@\IZmI-8AqUةXUfA1ڬV'Iٿך:TM B9fInմQlaLRjrq,qjl8s%Z,ЂXחm4E= RG 5EstA HazZrΑsDž^Eb?|#5lLŅ[ܺO WO)Ic+kYPp~|P)lD1UUK"VW ,nßRG^EZ 򤧚])Vd_  >KP1pwQ6B b_, "j5L` WBյKT9t"[/U1"ߙehSvUE'8r~א(q$R!@'YȀQ C~;|HԴGV{cc"ɶ|R/w*Y͜Ϩa*dVRXs PD)Ld$<HJę3AJҒ!,2/`*\0Hŋ16qJƏ A.8Dȓ()! Iʗ 8͌Jq@D:ȰӧMJիXjڲׯ`ÊK׮fӪ]۷`ѦM"t{W\ >X௅6, qB>+/[# @3XΞ6.;uli-]v4gʞM*sͻߺ.$"wzq.6zX'/Cß/'H0{5rA qubAz1HxH&Hwo0(oh#VcheތDVa:&dX>g)P&YMQJI$\ZYU!,2/*\P ICŋ3fD "D#C@#Sd4+c\085 O@JXha5cAPJeիXjʵ+VhŠKٳhǂM˶۰kʝ -w/^mE0X E @qZQ+l!U# Y<-:\fQU뷚_V뵶Wtͻ `ƒ٭wy8|Mr0X&ڹ{^Xŧ>=yg={{}s=YX姠}Ձ@lvg!][d%n؝vq n*nF,Xy37܍8Hn(!rgdo"lCU$GvK6iB5%nUNJj)&su\!,2/*\Aŋ3f "DC #Sh$ +c0"85h#O2hEa5JcAPJe萇իXjʵ+VAŠKٳhǂM˶۰kʝ |w/^m}Cb ~aVpq @XΞ˵jtYӋvkykʞM*sͻߺ_N"dǙ[xr"!qXblhݦX6k X8&FHYCXdf)i%T:ڑaEy۔VieX%] x&W!,2/*\0OHŋ1€BƏ A8@ȓ() Fʗ S8͌ RJ@DȰӧMJիXjׯ`ÊK׮fӪ]۷`ѦEt{W\](,al ,pƈB8 d^Μ٩cAҧSTM۸sYo`Q̾+0.t]:ӫOglS~[ :Z~w߿+poVz' \  |p@BueW!X!wyw~! ""0Li⋼&Uq"d8b@a7W:ȣdLchI$=: $XJU!,2/*\30Hŋ1jBƏ AZ8p@ȓ()F @Cʗ 8h͌F"JQ@D6ȰӧM:JիXjRׯ`ÊK׮fӪ]۷`Ѧ @t{W\ NX௅6,AqB>+/ەŬ,絍N;ZlipSV͚װcSP۸s[׀op ÿ^ ႆկSs`/>zfX6W'/ß/I}U@ ,{[ (EVs%\ !,2/h*\P|@ŋ3f a "C)#S8+c|0858 O *xha5cAPJePիXjʵ+V!ŠKٳhǂM˶۰kʝ -w/^m(X E8`%}:3V;w{i]@@Onr¹@y׫'o cvҽjo]~wv W_ cu%A!X*pp @cX@N]zBۇ ڈ$NE\U$畊҅/HȚ%ahcZ8cb=FFy!,2/PH*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ J`@! ,2/h@*\$8@ŋ3f,0CC@#S(+c$D87FŒ Z1@D\PӧP:L@իXjjuA`ÊKٰ^Ϫ]kڶpmbkڷjk+j}`mkt/VYdA=YΧS/ʺ몖ʞb5M`/o{w ʙ;nxXءgO={s_N;wq EvJ^?~*hW'`\ d%A1 *n ]cX@^XuFۈ$dk(hr!"X*_X/Wlآj5k3&8֍8k#FEl9 d\ ,iV !,2/*\P?tHŋ1@Ə AJ80@ȓ() Dʗ 8(͌" J1@D>ȰӧMJJիXj2ׯ`ÊK׮fӪ]۷`Ѧ@Xū7\q(,a0X cÎ] NXʖb^Աņ 4gӨo]ͺ5Ub˞M۸gwNZvA!\@Ἷ: A!?tɽ.:u{Vrūۙ kO~|.%@lG~# zfQ8م1ơ!|ajXbr⊺Uev"jUr,(ۃH#Y7cX;j磏@B(diIPkG=69ߓPNeVv!,2/*\0t@ŋ3f "C)@#S¤+c085r"O!hEa5:cAPJePիXjʵ+VŠKٳhǂM˶۰kʝ m "w/^m(X E8`EfdXBH$f%iT6ړDy۔i%X\ x&W!,2/*\d`@ŋ3f "DCi#Sd +cl085j!O"hhE a5ڠcAPJeЁիXjʵ+VŠKٳhǂM˶۰kʝ m(w/^m}8Ab ~ЁaVpږŬy,gZ5;ltSVͺװc_P۸sv P[/Ƌk8xXb/HpAt֧W^oor3( =Ꮟ?@ߣ|U@ _G\YYWvZXz_X 7aj@"zA*ۋ0l4ֈUg-ƢX6֘`Y;>'㐶MdA4HYDFiHx9 Q9UUdr@@!,2/*\0OHŋ1€BƏ A8@ȓ() Fʗ S8͌ RJ@DȰӧMJիXjׯ`ÊK׮fӪ]۷`ѦE t{W\X௅6,AqB>+/[ @3XΞ6.;uli-]v4gʞM*sͻߺ.X"'<9}0Å `XǮz٥OۡwћVsԽO_oO^w߁p P+(E $tAXiwjH7W+wa€&'4Wۍ8V1# GVDv=yA={ }s=YX姠}ʅ@ vg!]4tFd%n؝bq Ҁ*n(F,Xy37܍8Hn(!rgdo"lCU$GvK6iB5%nUNJj)&sa\!,2/X`*\0 )Bŋ3flR ")C@#SHd&+ct0856H$OCFpha5cAPJe0իXjʵ+VMŠKٳhǂM˶۰kʝ mGw޾lD-a ~0b6n+8^y΋Z5;litS3V:-dc˶ ۸sͻ7ʭ7qv$ƛ wpzBq.6zX'/Cß/'H11{5r1DP`qub@z1ȄxHa&Hwo0(oh#VcheތDVa:&dX>g)P&YMQJI$\ZY!,2/@*\Hŋ1 NƏ A8Cȓ()! @Pʗ 9h͌bJ@DȰӧMBJիXjRׯ`ÊK׮fӪ]۷`Ѧ$a@t{W\X௅6,A(qB>+/= @3XΞ6.;uli-]v4gʞM*,sͻߺ.6 A"Ϣ<} 2P0YǮz٥O wћsԽO_o%O^w߁pe!(EV!?rHXiwj7Wwa &'4Wۍ8V1# GVDv ]> ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-green.sv0000644000175000017500000001144700000000000035321 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/stack-red.svg0000644000175000017500000001326300000000000035140 0ustar00jamespagejamespage00000000000000 Compute Compute Load Balancer ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-gray.g0000644000175000017500000002345200000000000035352 0ustar00jamespagejamespage00000000000000GIF89a\\аǰ㸹ª񯰰즧୭ّ͂Ӡ򖘘rttcee{}}jlluww|~~y{{󱲲봵ﷷءÜȑ׽𾿿ẉ̂suu·ꃄyzz! NETSCAPE2.0! XMP DataXMP?xpacket begin="id:805D6FD7EA7311E2A8569C25BE948F7D"> =<;:9876543210/.-,+*)('&%$#"!  !2,\\H*\ȰÇ#JH⅋/^ȱ+J 8BIHF$PѣˈBf4Ar͒H 'B7I2AAPS&4!q!FXV@ȚLdZ)T &h]LN"2,D%gsm˗'̰LH˸gG KT33~ |%`|c41٤.+P\@M/VZ׊Ʃ 3+oK`8.w/NV^R)Ʉ")NGZGBI+F Pt fI 6(BǐHVa|E}Xas$MWha\GЁxx6_6~ b y bB14[m3eXZ9t!B\yN@l*Rᧄ3%S,矀9O ܃@@Bzg.1#,9^9Zo5NZ)^tHEXЃ yꟗV!g=8A0nYWњZ:ez"AEFh-N(Qs5j"I Z N%Q-WYvb2 I$-E-p_/I6p L0ckA_5;';q_ӆ%<-{\0sC8PF12jBBKRjFBmFq9\I;K$5I2$<ӡB-+[iLcЙu*S;h3og=*d!Jw8NJBd HXw2dGI'R a~Q'fʡo+A$!F#AC=\bp'JR ΅2$ePR@/@U}c @qO~GC~<"#bG $tduHC"gMDB#}HA}"#F&I\Aa  /(Dg@s eI$C/$ۃ3^,Ir#ŘA\Rp+6 *eF9KMMd&\NvRΞD$LJFN萞̧>Yπ!,#$dHXȐ"Jx ɔ3^DRÅL$LR\$HddɅ'Yꔄ A. gr\ٳ2=jR=ԙt PF^Ū~,ʇEp\Pڏ "a˷/&$$k#c%MɄ4h]XÉ)WN8䫚+d祡革~}7gf+yO-Z(m,qC6J#7ykν;w 7{4%*W'o}Jُ==xT?7y|XEad`ITQW|_БWMa Y}ӇȐS \6WG%,("xi 4 ~ɂL$DN@@!,#$7(HOX'"J(‚3^AÅJ R\2 ~dɅ'Yᄂ?|aDr\S2=jR=!PF^Ū@~,zEp-0ڏ$Rd˷o # kcAP4h]XÉ)WN8䫚+`祡树革~7gf+yO-Z(m,qC6J#7ykν;w 7{V`"q*W'o}Jُ==xP7yhPBa(`ITQW|_}БWMa$Y}ӇȐS$\6ާ#,("xi 4~‚(L$DNB@!,#$!HWXP+"Jh'Ο3^#aC7 x@(S4H1bx9raI8 XǍG @S͜)wtB4R, uԪV@׮>(Z6ќ]x#xsaAA0?E~INm\1d 9 (#80!D$]4AqZ%͈/Nyq0_V9epSW,F8˘@د-?mtG)O|x̓w'n_Un&RE|mVt`NpK1HG=e|Ȕd`HT!Cd}@ġ׍D革~7gf+yO-Z(m,qC6J#7y kν;w 7{& !1*W'o}Jُ==xP7y(@Aa`ITQW|_БWMaY}ӇȐS\6W ,("xi @~8 L$DN@@!,#$'HXȰ 5"JC3^r# ÅE5d-]R\rˣG9DdɅ'Y%F0ѡr\3#52=jR=iPF^Ū~,Epڏjm˷-!kc84hE]XÉ)WN8䫚+a祡΢1革~]7gf+yO-Z(m,qC6J#7y$kν;w 7{(q *W'o}Jُ==xR7yHaE`ITQW|_ёWMajY}ӇȐSj\6 ,("xi Ԉ~ȂBL$DN$D@!,#$#lHmXȰ 6"J3^ġcÅ-r̈𱤍/R\ `dɅ'Y|QƆ[ar\s 2=jR=옱PF^Ū~,Epq萅ڏ(˷o 3!kCc_؀4h-]XÉ)WN8䫚+aP祡革~-7gf+yO-Z(m,qC6J#7y.kν;w 7{@# *W'o}Jُ==xүQ7y1Ca-l`ITQW|_БWMaY}ӇȐS\6޷3,("xi 4~H*L$DNB@!,#$HYX0D ,"JF3^TÅ!q$ *tR\J<dɅ'YC COr\H 2=jR=PF^Ū@~,ʅEpՠ谎ڏP˷/5x#k;c4h!]XÉ)WN8䫚+cP祡革~}7gf+yO-Z(m,qC6J#7ykν;w 7{#*W'o}Jُ==xҧR?7y\Da!`ITQW|_БWMaY}ӇȐS\6gx,("xi ~hǂL$DNTA@!,#$YDHUXF*"J#ȏ3^ aXxH(S$2dH6*9raI8QGXLP͜)wB4Rx, uԪV׮B(Z6+]#x 2a0ȏE~INm\1d 9 +(#8>D$]4j qZ%͈/Nyq0_V9epSWlC8˘nAد-rltG)O|x̓wD'n_U=fCRE|mVt`NK1HG=e|Ȕd`+LHT!Cd}@إ^ADڠLbiY<9h/=$[E?Ƶ1yR⅁@ S|`}B_WI(_1$Khmh^}"gfXSBxFވG[Mf&@\_reDDiDF!,#$YDHUXF*"J#ȏ3^ Å6zL` "ER\Ȑ!ڨЂdɅ'Y,BDEmbBr\Ȑ2=jR=PF^Ū@~,*Ep谂ڏ1˷/ \!kC c4h6]XÉ)WN8䫚+^ap祡革~ 7gf+yO-Z(m,qC6J#7y-kν;w 7{#1*W'o}Jُ==x'R?7yBCa6D`ITQW|_БWMa1Y}ӇȐS1\6g ,("xi 4^~Ă;L$DNC@!,#$ HYX0†+,"J؃3^a |xɢ(SSl9raI8a GS͜)w(raB4R|z, uԪV׮\(ZȖJ]xJxaA0VE~INm\1d 90(#8wD$]4=qZ%͈/Nyq0_V9epSWA8˘@د-2ltG)O|x̓w}'n_U< ARE|mVt`NpK1HG=e|Ȕd`HT!Cd}@|إ^TXD革~7gf+yO-Z(m,qC6J#7y kν;w 7{& !1*W'o}Jُ==xP7y(@Aa`ITQW|_БWMaY}ӇȐS\6W ,("xi @~8 L$DN@@!,#$!lHWXP9+"JM3^lEÅ:5sR\rM4@#dɅ'Yꜳf7ur\SE92=jR=PF^Ū~,ꃢEpۨڏr˷6p"k7c؂4h:]XÉ)WN8䫚+b祡f革~7gf+yO-Z(m,qC6J#7ykν;w 7{ޒ"*W'o}Jُ==x_S7y>HFa:l`ITQW|_БWMarY}ӇȐSr\6ާBp,("xi G~ǂ ]> ././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-green.0000644000175000017500000000437600000000000035345 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/unknown-red.sv0000644000175000017500000000437600000000000035370 0ustar00jamespagejamespage00000000000000 ]> ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-gray.gif 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-gray.gi0000644000175000017500000001320000000000000035321 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄OqhIz爕kۄXCk80=hBB.ʛph0ZQ|ȅo Zs<"RG}{kiz+-p)y=~䯴1N]dAM;Ʋy"mx&N (đ>*܏UoK4Ec^SU GTk`Ȅ]N!C`@;e1E|(P2uhT<(u u;R1_fp̀2$ZAAw%4nȏ1)~9Gc̖f&.搦 m!.4x uBDVndYhx|e.1b()8Yi!nG}0)H# C j1Q)jsJKq))&hQLv|({ .Yv;dzj)q|A,)ڡ)QLJxkpN"{0>u.sl.x0p O´ G(Ks`b&&1$޲1bzF‘PxX2 5<ѓJ)iU'Y+"ZcR+/w9K@cYXjG#C*ѩR\ 9e3Bm JKIZNĤ:9Ozrk  H'?zG&{ tZ`sO)lҜ @]*S]na6e) L8#0EdCt7(&쮤USjp'LBX>`}Xb1!W"ޯq%"/fL 7T>ޭ<#B<)[`{UE!*bOFJX\-&HI]$qiعBm& ѹ4t}!Rn+y_IwĞ&`g&ӅγKn:F=>&6FZ2HṾf8{kVGĦeR*.u6>L|# hwpzQQ9\} IA0$N4i:[do'tp-꜄M? "qm9X YWm;e6ktW{qg!Ki±(F~͜HXi8ߢ<'tѭ:) |ne] X1(^}p;pY[MPb 9j$b;ћMs(F- Y  #ƒ X'0 $Ѱ!dI !@Aby" )q,Y ry.m*4D Jc;RgyZɡ`p_OpObr P)p"6+Pz П u_r0-' `vѠ$p}#0 `c  뀧" z tQ`Z JЦjZ(J&9!:АX!pp 09)$. ` S:A/A `  p>Z;OJBʋzz {zrNC {/`:v pm 0j `+.02;4[6{8:&A;././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-gray.sv0000644000175000017500000000524500000000000035364 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-green.s0000644000175000017500000000524500000000000035334 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/volume-red.svg0000644000175000017500000000524500000000000035343 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn-gray.svg0000644000175000017500000000460200000000000035023 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn-green.svg0000644000175000017500000000460200000000000035161 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn-red.svg0000644000175000017500000000460200000000000034633 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/vpn.gif0000644000175000017500000001041700000000000034032 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄Oqh;]?b=?kν> (h\?bR ^*o׿2`}r4S/? sQ@J,dܴCNPuH 轧Y> @8 %ΈSr'[X#!v]=Uz VZ/Vs5F@ČMp<|顀36!D< )ܘiVH"b\+I>pteJ%Q>;= TUzhz($I>L.t8?9zrr%+ӸQ"ϴARK$.}6+ K7 -ȉUv/Y#&AA3\H_93`͜ 3%cB|q xM3r)(;{M#%t(. &tܨL  w.X>:b68CZ")KS&HWDtGUi wp]ujT4F:7T&UXN㒸T78T9?*@U9D2J\[QZ.ծ^*X 뺿U+J U`d8/*ֲF!(^Ӥ<(isYjAښ(֓ o(>.mhqvMmr!*\6E*PZң.a Xu|l[gZqZlli]=/q]E7fv0>-z޶׽ʺJx\nVnu?|j$5-ni cz^R9yb!8E[/ٓ(Wx"v2|+?x~,rn, )UufysݠO+ujyʲ=2@ymvs|Dcҵ1XִcGC$QaOCIύR5*YSѡ6A})[ױֵ N {լD1H2m3xNp}lkӚݖMj@Q?=m .tޘ y}s߯}p'!u'}+L|=q*tφ_{-%Er+;٭Cbn٫8WΘ3E4LZ Å}QN P:If /E@vxy`~(zܷv4wIwŽZ~r}h˺ ٜ'~Bx縲k׼nDoʛ=gu7K h}䜖nn{y)۰\?7N/҄>Zo_R֐{O~0G;././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-gray.gif0000644000175000017500000002041500000000000035132 0ustar00jamespagejamespage00000000000000GIF89a,,!2! NETSCAPE2.0,,,0I8ͻ`(dihlp,tmx|pH,Ȥrl:tJ>؟zx|hnO8z஋|nhk~9g/[]%\QoU1<~mMXwuGȶCƛ>:52 + yz#rxg`)!h E fѢA~;x2ȑPܗʕl t/kYK> tD5i2FWE}gTYX-45 Wyر6Yfzcqtwٽw|7셍Ō' C Z%+G6ͳ # zsbKJ=Z깟1~ qҴguվ;8kK2=?_yiwڀ hB{!HUM(aev/lUւtȜ"w.V㋡a(4fc=;昞c}6$ސ UH֨$ >٤ Y02X(dZvߕ[~)&\ȡg)1)')T*(q~BFe@jitf7vz*%꣧N$*ʊ^B ꫞:鰺jz,kb*-Rc&ZYk.+.Af`ޫ/ <0ؾ9 o /V|Ҹk1:OAml/Jp4۲%qQs<*Nj4|4P\tU g]X{=1gq3lusi3fGt~mmg}.">t ak=v mM3 C:ʣn8>sٺϋ®q3 lEI˚]ÝOn2>𲳏 } }OS_G̽R__}_*>@m˕Y<@WpwsWB~O-Aީ.ZX /y1jSa$HBΐr2Cٰxkak8ĒTDs<DMxAۆmQ'b(,:qZDhQUl4Un5QLqѓGc H> R_|M@H8W"W "1\$IlT'{ɱ\& |Q҆R&,qN`T"erʺҔ-%Lf! ^e9LQҗǼH5Mlݼ7{ζsK:ά LݱL3~$D(@j,5?N΄OqhVð8{QnRh˺wxx钷uvx`sv}~H^ȵɾiijopqտд؞ܮɸH`Pemnp?X෸ѯų񖘗пǐƸ )&)&J& ٷ+-*$8 ;#I L H`"toÇ*\ ŋeXPďR\pɓ(8zɲWNʜy-sƂAϟ%]8 H])JŏXZ`bWJ=;׳Ug W*]@1s8* `G |ЊzHܲDSv򠔩:b-:D 6vi0"߂?3tp]le*JxLyDfJ/" :\ 3VaP1]m?!:d@zp '!VF!CA\(ĚDpg$r fdd)*I<c%oLx72+8hB.[I&:dN>r^FT!0I[6B+9}.) f]Ԥ&g;D%A&f4')%Bi jYFUXnY'3Z'\ 6b@:ʥ*:EJJ k(+ekJ p _yQӞu+:b F(9շ`*n)*UE֮~jNWDKОY0D) e/,P- E>TKPq>&\ɮ>̳LQsP@8h@v$htXAx1ZP,ˆ(#A2@V LP(D{@A4 +` T7Q̅eI,BacH8sRc(z60H)Ph1F!M ańLʵA(\nHBلٽXB%(λخ68&μ@zBWc'zlJ\p d8n'>pn;'L |;ܠ "}oag yT€ c2 Q[X`ĸ@'Zam!t FtLAX%E;[,fab!Ӣ#ZWP@m$YbLWR1i{+=J$p8=R̋ _.D"[+&s6nuICȝLC! p,, 4.`x41[QCE!̿6YDdc 6DE ^ pl!AB 60׵>y~{ "R, H]!!=I zQ7Na2I1LV 7mz4> QU%\3mD b݆ `5 Aƚ"3 4Ɔm;A<@+7vX*A(!C4H !]2k :uWV`AڎHO% ml<|V~*|yÏH6i?Dɀh5x >|?Du bf&vķ1{cAC ialF47F%- <|SF=!'~HZ?x‹Bp m 9uꈸ nʇ\PPHpOy,z@o@XXP!Ug; T^p_~5  q~g"B2 Pkqp1I0E1rsL1][uQF%C +eya.g\+_pC46w0Ub)guhhSاkȧN40~,m}@x @DH< Q0t` a7MW`Dd xGPg!0XptcP y[( i `9(8GL vzRq:'q@ `g0DI,0z@%X2HcT gX7@^b\Ol{Q:( @%)˜1`%,PJgqD=&UP\4$Tf4!@Hcш1B0~M~6TlaF{VWyO!Qz`.pX y y@(2pV_iMǧ}MUg9ďhҩNy'Q=I{pR&!Fpk21Å`Xy9m)M w_y3!]𔑠AInN-^"2J 4 @-ٖ8 sY0kxJRPԤEG}W"y @0蠤0l|xQY朂eW~j tB3%(m@qEEQB:{n,r_QѪ q"` vSQtp@:2, = !70p$Q|R4wX:9hF0( 헃Vߙʮ!RAp@::z`F1Gǣ1 a{Z}^Z Lpp鐌Wr b\-QkP"ױ,ǰBx H}감.y[# PŠefX5`=X{  ^+=A`=jҚ p) !+pK0u n ٵz1=6Ԛoж*>h K֒pư꠲wK ӬIfAu/c j b1 P廿 gA N/򪾒1YGPu >K+x⊖X| 9Kp 2P +6;s \ :ۺlykJI09pq`=0L{K )=%\IM rg J L l`r9@/@Pı + ɎY q7j5 ` HrL-L c` Zh ? ǂ{; ] qY jyX1 HL Lk| K xn<ܼNL I>W}E XP!{3Ɗ0KGplͥffp[ "Aڜ| (ˢI C|cСR` L\ ) bjU <=d˵2Dg[ e C|9*! aΘM \}-|3#[c,  ͪ\pyƒҜʴ ; W ;PÃD ю}F 00?3 PrKbǙq 7PTWˠT; +E |}z- < ` =ү^|=< I*7 ' C,֜:EZۯmFکc۸mE@ۼ p70۠Q0T նK K ؝1 ]10p1V[W -@@ɤw!ŖwМ&Jj!vɺ<&׏pҟ #VvqvL 0-ż 1ߧp+} p#*_A- K :| %ޞz)7UZG=h 1b P7_A 9>@>‰-A=LOQ>F m;Gtxz~( Gr~ zؤ1l8# g%p] . eE~Uk=@d~ jZ ꡾C-e Nd~ Vc̸?DmRr % s ,.Dě> >S^B ?~$KԷp z*_ y_zS 4N(^ p n5$% 0ߢ0I#S *>  'ɦnFot%*,{Fk 1+ont8^1qSHY 'ܣ I(Jb`͇ϟ|S^ re1 sgSL c ~@+. Фv cRbkrm  /İbP`ðp/ep 堟 i }Cr `pr=n`^- >31ِSC@pvO Oo9Q, 8HXhx PiyiQ 9Zzh@: *;B;{Y+k{1)bޫʜ;2=  w!GU@(GxJ)RXm?*-A5)> իW;1"n {a`WCPB1tEhuA,{ C^҈"~-0Eyvѯ$|l\*)`vUQP|ߣ;{[B 0$YiDh YWIz$dP:@keP %h %D@?̌  ۆ1h `@-J @@ .\ah2ZVH#)# \%\qCBA+Z .]Yr &~n Ę瓈g-p_vNj#lh$:z Hi\(,P H-%,Cډ #I,&08/ҾA\B&lKn枋nn oKo;././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-gray.svg0000644000175000017500000000554400000000000035172 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-green.svg0000644000175000017500000000554400000000000035330 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/img/wait-red.svg0000644000175000017500000000554400000000000035002 0ustar00jamespagejamespage00000000000000 ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/0000755000175000017500000000000000000000000033251 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.5810838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/0000755000175000017500000000000000000000000034711 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000024700000000000011460 xustar0000000000000000145 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/actions-checked-selected.template.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/act0000644000175000017500000000026700000000000035410 0ustar00jamespagejamespage00000000000000 $text$ ././@PaxHeader0000000000000000000000000000022100000000000011450 xustar0000000000000000123 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/check.service.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/che0000644000175000017500000001022500000000000035373 0ustar00jamespagejamespage00000000000000 (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; } } })(); ././@PaxHeader0000000000000000000000000000022200000000000011451 xustar0000000000000000124 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/create.service.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/cre0000644000175000017500000000564100000000000035413 0ustar00jamespagejamespage00000000000000/** * (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 ././@PaxHeader0000000000000000000000000000022200000000000011451 xustar0000000000000000124 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/delete.service.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/stacks/actions/del0000644000175000017500000001035500000000000035404 0ustar00jamespagejamespage00000000000000 (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; } } })(); ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000111 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/ 27 mtime=1585236225.553084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000021700000000000011455 xustar0000000000000000115 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/css/ 28 mtime=1585236225.5810838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/css/angular-material.scss 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000203720600000000000035577 0ustar00jamespagejamespage00000000000000/*! * 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 $}
    ././@PaxHeader0000000000000000000000000000027200000000000011456 xustar0000000000000000164 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volume/os__cinder__volume.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000672100000000000035573 0ustar00jamespagejamespage00000000000000(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': '<', } }); })(); ././@PaxHeader0000000000000000000000000000027700000000000011463 xustar0000000000000000169 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volume/os__cinder__volume.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000561100000000000035570 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000027300000000000011457 xustar0000000000000000165 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volume/os__cinder__volume.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000536300000000000035574 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000026500000000000011460 xustar0000000000000000153 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/ 28 mtime=1585236225.5930839 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000032400000000000011454 xustar0000000000000000190 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001312100000000000035563 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032400000000000011454 xustar0000000000000000190 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001312100000000000035563 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032500000000000011455 xustar0000000000000000191 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001311100000000000035562 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032300000000000011453 xustar0000000000000000189 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001312100000000000035563 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032000000000000011450 xustar0000000000000000186 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000424300000000000035570 0ustar00jamespagejamespage00000000000000 {$ iuuid.name $}
    You must supply an instance uuid.
    {$ vol.name $}
    You must supply an volume id.
    Please input a valid device path.
    ././@PaxHeader0000000000000000000000000000031600000000000011455 xustar0000000000000000184 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001370500000000000035573 0ustar00jamespagejamespage00000000000000(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': '<', } }); })(); ././@PaxHeader0000000000000000000000000000032300000000000011453 xustar0000000000000000189 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000602600000000000035571 0ustar00jamespagejamespage00000000000000(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'); }); }); })(); ././@PaxHeader0000000000000000000000000000031700000000000011456 xustar0000000000000000185 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__cinder__volumeattachment/os__cinder__volumeattachment.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001311300000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000026100000000000011454 xustar0000000000000000149 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/ 28 mtime=1585236225.5930839 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000031400000000000011453 xustar0000000000000000182 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000212600000000000035566 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031400000000000011453 xustar0000000000000000182 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000212600000000000035566 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031500000000000011454 xustar0000000000000000183 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000211300000000000035562 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031300000000000011452 xustar0000000000000000181 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000212600000000000035566 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000031000000000000011447 xustar0000000000000000178 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000667700000000000035605 0ustar00jamespagejamespage00000000000000
    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.
    ././@PaxHeader0000000000000000000000000000030600000000000011454 xustar0000000000000000176 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001015300000000000035565 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000031300000000000011452 xustar0000000000000000181 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000336700000000000035576 0ustar00jamespagejamespage00000000000000(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"); }); }); })(); ././@PaxHeader0000000000000000000000000000030700000000000011455 xustar0000000000000000177 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__recordset/os__designate__recordset.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000211500000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSRECORD././@PaxHeader0000000000000000000000000000025400000000000011456 xustar0000000000000000144 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/ 28 mtime=1585236225.5970838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000030200000000000011450 xustar0000000000000000172 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000176300000000000035574 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000030200000000000011450 xustar0000000000000000172 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000175700000000000035577 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000030300000000000011451 xustar0000000000000000173 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000175000000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000030100000000000011447 xustar0000000000000000171 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000176300000000000035574 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000627700000000000035601 0ustar00jamespagejamespage00000000000000
    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 $}
    ././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000634400000000000035574 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000030100000000000011447 xustar0000000000000000171 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000371600000000000035574 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__designate__zone/os__designate__zone.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000175200000000000035572 0ustar00jamespagejamespage00000000000000 Layer 1 Layer 1DNSZONE././@PaxHeader0000000000000000000000000000026300000000000011456 xustar0000000000000000151 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/ 28 mtime=1585236225.5970838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000032000000000000011450 xustar0000000000000000186 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000470500000000000035573 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000032000000000000011450 xustar0000000000000000186 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000460000000000000035565 0ustar00jamespagejamespage00000000000000Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000032100000000000011451 xustar0000000000000000187 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000467700000000000035603 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000031700000000000011456 xustar0000000000000000185 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000470500000000000035573 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 ././@PaxHeader0000000000000000000000000000031400000000000011453 xustar0000000000000000182 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002055200000000000035571 0ustar00jamespagejamespage00000000000000
    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.
    ././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001302700000000000035570 0ustar00jamespagejamespage00000000000000(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': '<', } }); })(); ././@PaxHeader0000000000000000000000000000031700000000000011456 xustar0000000000000000185 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__autoscalinggroup/os__heat__autoscalinggroup.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001104200000000000035563 0ustar00jamespagejamespage00000000000000(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 ././@PaxHeader0000000000000000000000000000026000000000000011453 xustar0000000000000000148 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/ 28 mtime=1585236225.5970838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002416500000000000035575 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002416500000000000035575 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031300000000000011452 xustar0000000000000000181 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002414300000000000035571 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031100000000000011450 xustar0000000000000000179 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002416500000000000035575 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030600000000000011454 xustar0000000000000000176 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001350500000000000035571 0ustar00jamespagejamespage00000000000000
    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 $}
    ././@PaxHeader0000000000000000000000000000030400000000000011452 xustar0000000000000000174 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001261400000000000035571 0ustar00jamespagejamespage00000000000000(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': '<', } }); })(); ././@PaxHeader0000000000000000000000000000031100000000000011450 xustar0000000000000000179 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001204000000000000035562 0ustar00jamespagejamespage00000000000000(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(); }); }); })(); ././@PaxHeader0000000000000000000000000000030500000000000011453 xustar0000000000000000175 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__resourcegroup/os__heat__resourcegroup.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002412300000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000026000000000000011453 xustar0000000000000000148 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/ 28 mtime=1585236225.5970838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000511500000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000512400000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000031300000000000011452 xustar0000000000000000181 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000510300000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000031100000000000011450 xustar0000000000000000179 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000511500000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000030600000000000011454 xustar0000000000000000176 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000627300000000000035575 0ustar00jamespagejamespage00000000000000 {$ 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.
    ././@PaxHeader0000000000000000000000000000030400000000000011452 xustar0000000000000000174 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001250100000000000035564 0ustar00jamespagejamespage00000000000000(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': '<', } }); })();././@PaxHeader0000000000000000000000000000031100000000000011450 xustar0000000000000000179 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000773300000000000035577 0ustar00jamespagejamespage00000000000000(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"); }); }); })(); ././@PaxHeader0000000000000000000000000000030500000000000011453 xustar0000000000000000175 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__heat__scalingpolicy/os__heat__scalingpolicy.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000511000000000000035562 0ustar00jamespagejamespage00000000000000 Layer 1Layer 2 P ././@PaxHeader0000000000000000000000000000026000000000000011453 xustar0000000000000000148 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/ 28 mtime=1585236225.6010838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000421200000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000421200000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031300000000000011452 xustar0000000000000000181 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000420600000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031100000000000011450 xustar0000000000000000179 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000421200000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030600000000000011454 xustar0000000000000000176 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001215000000000000035564 0ustar00jamespagejamespage00000000000000 {$ 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.
    ././@PaxHeader0000000000000000000000000000030400000000000011452 xustar0000000000000000174 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001574100000000000035575 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000031100000000000011450 xustar0000000000000000179 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000707300000000000035574 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000030500000000000011453 xustar0000000000000000175 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingip/os__neutron__floatingip.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000420600000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027300000000000011457 xustar0000000000000000159 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/ 28 mtime=1585236225.6010838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000033500000000000011456 xustar0000000000000000199 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingassociation.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000573100000000000035573 0ustar00jamespagejamespage00000000000000(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"); }); }); })(); ././@PaxHeader0000000000000000000000000000034000000000000011452 xustar0000000000000000202 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000704300000000000035571 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000034000000000000011452 xustar0000000000000000202 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000704300000000000035571 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000034100000000000011453 xustar0000000000000000203 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000703300000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000033700000000000011460 xustar0000000000000000201 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000704300000000000035571 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000033400000000000011455 xustar0000000000000000198 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000410000000000000035560 0ustar00jamespagejamespage00000000000000 {$ fip.id $}
    You must supply a floating IP.
    {$ port.name $}
    You must supply a port.
    Please input a valid IP address.
    ././@PaxHeader0000000000000000000000000000033200000000000011453 xustar0000000000000000196 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001302500000000000035566 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000033300000000000011454 xustar0000000000000000197 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__floatingipassociation/os__neutron__floatingipassociation.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000704200000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000025100000000000011453 xustar0000000000000000141 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/ 28 mtime=1585236225.6010838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000300600000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000300600000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000300200000000000035560 0ustar00jamespagejamespage00000000000000 Layer 1././@PaxHeader0000000000000000000000000000027300000000000011457 xustar0000000000000000165 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000300600000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1././@PaxHeader0000000000000000000000000000027000000000000011454 xustar0000000000000000162 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001257100000000000035573 0ustar00jamespagejamespage00000000000000
    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.
    ././@PaxHeader0000000000000000000000000000026600000000000011461 xustar0000000000000000160 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000657300000000000035600 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000027300000000000011457 xustar0000000000000000165 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000417100000000000035570 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000026700000000000011462 xustar0000000000000000161 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__net/os__neutron__net.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000300200000000000035560 0ustar00jamespagejamespage00000000000000 Layer 1././@PaxHeader0000000000000000000000000000025200000000000011454 xustar0000000000000000142 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/ 28 mtime=1585236225.6050837 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000430200000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000430200000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027700000000000011463 xustar0000000000000000169 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000427600000000000035576 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000430200000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027200000000000011456 xustar0000000000000000164 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002411100000000000035564 0ustar00jamespagejamespage00000000000000
    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.
    ././@PaxHeader0000000000000000000000000000027000000000000011454 xustar0000000000000000162 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002313400000000000035570 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001246000000000000035570 0ustar00jamespagejamespage00000000000000(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' }]); }); }); })(); ././@PaxHeader0000000000000000000000000000027100000000000011455 xustar0000000000000000163 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__port/os__neutron__port.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000427600000000000035576 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000025400000000000011456 xustar0000000000000000144 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/ 28 mtime=1585236225.6050837 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000030200000000000011450 xustar0000000000000000172 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000251200000000000035565 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030200000000000011450 xustar0000000000000000172 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000251200000000000035565 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030300000000000011451 xustar0000000000000000173 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000250600000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030100000000000011447 xustar0000000000000000171 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000251200000000000035565 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001440500000000000035571 0ustar00jamespagejamespage00000000000000
    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
    ././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001552700000000000035577 0ustar00jamespagejamespage00000000000000(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.networks.concat(resource_networks); } return $scope.options.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); ././@PaxHeader0000000000000000000000000000030100000000000011447 xustar0000000000000000171 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000752300000000000035574 0ustar00jamespagejamespage00000000000000(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'}], 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); }); }); })(); ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__router/os__neutron__router.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000250600000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000026500000000000011460 xustar0000000000000000153 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/ 28 mtime=1585236225.6050837 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000032400000000000011454 xustar0000000000000000190 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000567500000000000035602 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032400000000000011454 xustar0000000000000000190 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000567500000000000035602 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032500000000000011455 xustar0000000000000000191 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000566300000000000035577 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032300000000000011453 xustar0000000000000000189 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000567500000000000035602 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032000000000000011450 xustar0000000000000000186 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000360500000000000035571 0ustar00jamespagejamespage00000000000000

    {$ $ctrl.message $}

    {$ router.name $}
    You must supply a router.
    {$ port.name $} {$ subnet.name $}
    ././@PaxHeader0000000000000000000000000000031600000000000011455 xustar0000000000000000184 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001411500000000000035567 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000032300000000000011453 xustar0000000000000000189 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000613300000000000035570 0ustar00jamespagejamespage00000000000000(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"); }); }); })(); ././@PaxHeader0000000000000000000000000000031700000000000011456 xustar0000000000000000185 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__routerinterface/os__neutron__routerinterface.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000567600000000000035603 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000026300000000000011456 xustar0000000000000000151 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/ 28 mtime=1585236225.6050837 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000032000000000000011450 xustar0000000000000000186 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000334000000000000035565 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032000000000000011450 xustar0000000000000000186 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000334000000000000035565 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000032100000000000011451 xustar0000000000000000187 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000333400000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031700000000000011456 xustar0000000000000000185 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000334000000000000035565 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000031400000000000011453 xustar0000000000000000182 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001314200000000000035566 0ustar00jamespagejamespage00000000000000
    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!
    ././@PaxHeader0000000000000000000000000000031200000000000011451 xustar0000000000000000180 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000567100000000000035576 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000031700000000000011456 xustar0000000000000000185 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000421000000000000035562 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000031300000000000011452 xustar0000000000000000181 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__securitygroup/os__neutron__securitygroup.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000333400000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000025400000000000011456 xustar0000000000000000144 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/ 28 mtime=1585236225.6050837 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000030200000000000011450 xustar0000000000000000172 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000612400000000000035570 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030200000000000011450 xustar0000000000000000172 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000612300000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030300000000000011451 xustar0000000000000000173 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000611700000000000035572 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030100000000000011447 xustar0000000000000000171 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000612300000000000035567 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000002202600000000000035567 0ustar00jamespagejamespage00000000000000
    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.
    ././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000001263700000000000035576 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000030100000000000011447 xustar0000000000000000171 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000721400000000000035571 0ustar00jamespagejamespage00000000000000(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'); }); }); })(); ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__neutron__subnet/os__neutron__subnet.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000611700000000000035572 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000025100000000000011453 xustar0000000000000000142 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/ 27 mtime=1585236225.609084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000612000000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000612000000000000035564 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027700000000000011463 xustar0000000000000000169 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000611300000000000035566 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000611700000000000035572 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027200000000000011456 xustar0000000000000000164 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000570700000000000035576 0ustar00jamespagejamespage00000000000000
    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.
    ././@PaxHeader0000000000000000000000000000027000000000000011454 xustar0000000000000000162 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000526400000000000035574 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000230200000000000035562 0ustar00jamespagejamespage00000000000000(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"); }); }); })(); ././@PaxHeader0000000000000000000000000000027100000000000011455 xustar0000000000000000163 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__keypair/os__nova__keypair.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000611300000000000035566 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000025000000000000011452 xustar0000000000000000141 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/ 27 mtime=1585236225.609084 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000400100000000000035560 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027400000000000011460 xustar0000000000000000166 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000377300000000000035577 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027500000000000011461 xustar0000000000000000167 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000377500000000000035601 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027300000000000011457 xustar0000000000000000165 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000400100000000000035560 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000027000000000000011454 xustar0000000000000000162 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000006633500000000000035602 0ustar00jamespagejamespage00000000000000
    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)
    ././@PaxHeader0000000000000000000000000000026600000000000011461 xustar0000000000000000160 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000006510000000000000035567 0ustar00jamespagejamespage00000000000000(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); ././@PaxHeader0000000000000000000000000000027300000000000011457 xustar0000000000000000165 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000003175000000000000035573 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000026700000000000011462 xustar0000000000000000161 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__nova__server/os__nova__server.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000377500000000000035601 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000025500000000000011457 xustar0000000000000000145 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/ 28 mtime=1585236225.6130838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000030400000000000011452 xustar0000000000000000174 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-blue.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000364300000000000035573 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030400000000000011452 xustar0000000000000000174 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-gray.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000363500000000000035574 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030500000000000011453 xustar0000000000000000175 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-green.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000363700000000000035576 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030300000000000011451 xustar0000000000000000173 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container-red.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000364300000000000035573 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000030000000000000011446 xustar0000000000000000170 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000703000000000000035565 0ustar00jamespagejamespage00000000000000
    That doesn't look like a valid container name.
    Your container name is too long.
    Purge on Delete
    ././@PaxHeader0000000000000000000000000000027600000000000011462 xustar0000000000000000168 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000736600000000000035601 0ustar00jamespagejamespage00000000000000(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': '<', } }); })(); ././@PaxHeader0000000000000000000000000000030300000000000011451 xustar0000000000000000173 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.spec.js 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000543300000000000035572 0ustar00jamespagejamespage00000000000000(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); }); }); })(); ././@PaxHeader0000000000000000000000000000027700000000000011463 xustar0000000000000000169 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/js/resources/os__swift__container/os__swift__container.svg 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000363700000000000035576 0ustar00jamespagejamespage00000000000000 Layer 1 ././@PaxHeader0000000000000000000000000000022500000000000011454 xustar0000000000000000121 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/ 28 mtime=1585236225.6130838 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000755000175000017500000000000000000000000035563 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/depends_on.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000135300000000000035567 0ustar00jamespagejamespage00000000000000 Check following resources to specify the depends_on relations.
    {{ node.label }}
    ././@PaxHeader0000000000000000000000000000023700000000000011457 xustar0000000000000000137 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_draft.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000217500000000000035572 0ustar00jamespagejamespage00000000000000

    {$ 'Saved Drafts' | translate $}

    Last saved draft is {$ latest_draft.time $}. You haven't saved any draft yet! {$ draft.time $}
    {$ 'OK' | translate $}
    ././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_edge.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000405400000000000035570 0ustar00jamespagejamespage00000000000000

    Relations - depends on

    FROM

    {$ from_type $}

    {$ from_node.id $}

    TO

    {$ to_type $}

    {$ to_node.id $}

    {$ 'Delete Resource' | translate $} {$ 'OK' | translate $}
    ././@PaxHeader0000000000000000000000000000024200000000000011453 xustar0000000000000000140 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_resource.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000233100000000000035564 0ustar00jamespagejamespage00000000000000

    Resource {$ resource_type $}

    {$ 'Delete Resource' | translate $} {$ 'Cancel' | translate $} {$ 'Save' | translate $}
    ././@PaxHeader0000000000000000000000000000024200000000000011453 xustar0000000000000000140 path=heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator/templates/modal_template.html 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/static/dashboard/project/heat_dashboard/template_generator0000644000175000017500000000320100000000000035561 0ustar00jamespagejamespage00000000000000

    {$ 'Heat Orchestration Template' | translate $}

    {$ warning | translate $}
    {$ 'Cancel' | translate $} {$ 'Download' | translate $} {$ 'Create Stack' | translate $}
    ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6130838 heat-dashboard-2.1.0.dev23/heat_dashboard/test/0000755000175000017500000000000000000000000023124 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/__init__.py0000644000175000017500000000000000000000000025223 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/helpers.py0000644000175000017500000003624200000000000025147 0ustar00jamespagejamespage00000000000000# 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 import django 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 django.utils import http from heatclient import client as heat_client from keystoneclient.v2_0 import client as keystone_client import mock from neutronclient.v2_0 import client as neutron_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(RequestFactoryWithMessages, self).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(RequestFactoryWithMessages, self).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(TestCase, self).setUp() def _setup_test_data(self): super(TestCase, self)._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(TestCase, self)._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(TestCase, self).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): return 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) 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 django.VERSION >= (1, 9): loc = str(response._headers.get('location', None)[1]) loc = http.urlunquote(loc) expected_url = http.urlunquote(expected_url) self.assertEqual(loc, expected_url) else: self.assertEqual(response._headers.get('location', None), ('Location', settings.TESTSERVER + 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" % ["%s: %s" % (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: %s' % (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, 'is_ajax.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(BaseAdminViewTests, self).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(APITestCase, self).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(APITestCase, self).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_neutronclient(self): if not hasattr(self, "neutronclient"): neutron_client.Client = mock.Mock() self.neutronclient = neutron_client.Client return self.neutronclient def stub_heatclient(self): if not hasattr(self, "heatclient"): heat_client.Client = mock.Mock() self.heatclient = heat_client.Client return self.heatclient # Need this to test both Glance API V1 and V2 versions class ResetImageAPIVersionMixin(object): def setUp(self): super(ResetImageAPIVersionMixin, self).setUp() project_api.glance.VERSIONS.clear_active_cache() def tearDown(self): project_api.glance.VERSIONS.clear_active_cache() super(ResetImageAPIVersionMixin, self).tearDown() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6130838 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/0000755000175000017500000000000000000000000025447 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/__init__.py0000644000175000017500000000000000000000000027546 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/horizon.conf0000644000175000017500000000032700000000000030010 0ustar00jamespagejamespage00000000000000[plugin] is_plugin=True plugin_page_path=heat_dashboard.test.integration.pages plugin_page_structure='{"Project": {"Orchestration": {"_": ["Stacks", "Resource Types", "Template Versions", "Template Generator"]}}}' ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6130838 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/0000755000175000017500000000000000000000000026546 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/__init__.py0000644000175000017500000000000000000000000030645 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6130838 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/0000755000175000017500000000000000000000000030214 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/__init__.py0000644000175000017500000000000000000000000032313 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6170838 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/0000755000175000017500000000000000000000000033100 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/__init__.py0000644000175000017500000000000000000000000035177 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/resourcetypespage.py 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/resourcetypes0000644000175000017500000000151600000000000035742 0ustar00jamespagejamespage00000000000000# # 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(ResourcetypesPage, self).__init__(driver, conf) self._page_title = "Resource Types" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/stackspage.py0000644000175000017500000000147000000000000035601 0ustar00jamespagejamespage00000000000000# # 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(StacksPage, self).__init__(driver, conf) self._page_title = "Stacks" ././@PaxHeader0000000000000000000000000000021700000000000011455 xustar0000000000000000121 path=heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/templategeneratorpage.py 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/templategener0000644000175000017500000000153200000000000035660 0ustar00jamespagejamespage00000000000000# # 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(TemplategeneratorPage, self).__init__(driver, conf) self._page_title = "Template Generator" ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/templateversionspage.py 22 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/pages/project/orchestration/templateversi0000644000175000017500000000152700000000000035714 0ustar00jamespagejamespage00000000000000# # 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(TemplateversionsPage, self).__init__(driver, conf) self._page_title = "Template Versions" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/integration/test_basic.py0000644000175000017500000000346000000000000030144 0ustar00jamespagejamespage00000000000000# # 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') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/settings.py0000644000175000017500000000246100000000000025341 0ustar00jamespagejamespage00000000000000# # 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)) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1585236225.6170838 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/0000755000175000017500000000000000000000000025074 5ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/__init__.py0000644000175000017500000000000000000000000027173 0ustar00jamespagejamespage00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/cinder_data.py0000644000175000017500000002061600000000000027710 0ustar00jamespagejamespage00000000000000# 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.v2 import volume_backups as vol_backups from cinderclient.v2 import volume_snapshots as vol_snaps from cinderclient.v2 import volume_types from cinderclient.v2 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': u'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': u'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': u'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': u'1', 'name': u'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': u'2', 'name': u'vol_type_2', 'description': 'type 2 description'}) vol_type3 = volume_types.VolumeType(volume_types.VolumeTypeManager(None), {'id': u'3', 'name': u'vol_type_3', 'is_public': False, 'description': 'type 3 description'}) TEST.cinder_volume_types.add(vol_type1, vol_type2, vol_type3) # Volumes - Cinder v2 volume_v2 = volumes.Volume( volumes.VolumeManager(None), {'id': "31023e92-8008-4c8b-8059-7f2293ff1234", 'name': 'v2_volume', 'description': "v2 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_v2.bootable = 'true' TEST.cinder_volumes.add(api.cinder.Volume(volume_v2)) 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': 'v2 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': 'v2 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': 'v2 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/exceptions.py0000644000175000017500000000325000000000000027627 0ustar00jamespagejamespage00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/glance_data.py0000644000175000017500000003713000000000000027674 0ustar00jamespagejamespage00000000000000# 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(Namespace, self).__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': u'snapshot', 'container_format': u'ami', 'id': 3, 'status': "active", 'owner': TEST.tenant.id, 'properties': {'image_type': u'snapshot'}, 'is_public': False, 'protected': False} snapshot_dict_no_owner = {'name': u'snapshot 2', 'container_format': u'ami', 'id': 4, 'status': "active", 'owner': None, 'properties': {'image_type': u'snapshot'}, 'is_public': False, 'protected': False} snapshot_dict_queued = {'name': u'snapshot 2', 'container_format': u'ami', 'id': 5, 'status': "queued", 'owner': TEST.tenant.id, 'properties': {'image_type': u'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': u'qcow2', 'status': "active", 'size': 20 * 1024 ** 3, 'virtual_size': None, 'min_disk': 0, 'owner': TEST.tenant.id, 'container_format': 'novaImage', 'properties': {'image_type': u'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': u'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': u'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': u'a multi prop image', 'foo': u'foo val', 'bar': u'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': u'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': u'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': u'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': u'a multi prop image', 'foo': u'foo val', 'bar': u'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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/heat_data.py0000644000175000017500000004075400000000000027372 0ustar00jamespagejamespage00000000000000# 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(object): def __init__(self, data): self.data = data class Template(object): def __init__(self, data, validate): self.data = data self.validate = validate class Snapshot(object): 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{0}".format(i), 'DBPassword': '******', 'AWS::Region': 'ap-southeast-1', 'DBName': u'wordpress' }, "stack_status_reason": "Stack successfully created", "stack_name": "stack-test{0}".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{0}".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{0}".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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/keystone_data.py0000644000175000017500000003150700000000000030306 0ustar00jamespagejamespage00000000000000# 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 datetime import timedelta from django.conf import settings from django.utils import datetime_safe 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': u'\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_safe.datetime.now() + 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/neutron_data.py0000644000175000017500000001730500000000000030137 0ustar00jamespagejamespage00000000000000# 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_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 return by neutronclient. TEST.api_networks = utils.TestDataContainer() TEST.api_subnets = utils.TestDataContainer() # 1st network. network_dict = {'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'], 'tenant_id': '1', 'router:external': 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', 'enable_dhcp': True, 'gateway_ip': '10.0.0.1', 'id': network_dict['subnets'][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', 'enable_dhcp': True, 'gateway_ip': 'fdb6:b88a:488e::1', 'id': network_dict['subnets'][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.add(network_dict) TEST.api_subnets.add(subnet_dict) TEST.api_subnets.add(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 = {'admin_state_up': True, 'id': '9b466b94-213a-4cda-badf-72c102a874da', 'name': 'ext_net', 'status': 'ACTIVE', 'subnets': ['d6bdc71c-7566-4d32-b3ff-36441ce746e8'], 'tenant_id': '3', 'router:external': True, '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', 'enable_dhcp': 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']} 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', '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 = 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)) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1585236223.0 heat-dashboard-2.1.0.dev23/heat_dashboard/test/test_data/nova_data.py0000644000175000017500000001654100000000000027411 0ustar00jamespagejamespage00000000000000# 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 availability_zones from novaclient.v2 import flavors from novaclient.v2 import keypairs from novaclient.v2 import servers from heat_dashboard.test.test_data import utils class FlavorExtraSpecs(dict): def __repr__(self): return "" % self._info def __init__(self, info): super(FlavorExtraSpecs, self).__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": "